Merge branch 'main' into feat/hash

feat/hash
Eduardo San Martin Morote 3 years ago committed by GitHub
commit 95baacd2ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,32 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'bug: pending triage'
assignees: ''
---
<!--
NOTE:
VitePress is still WIP, and it is not compatible with VuePress.
Please do not open issue about default theme missing features or something doesn't work like VuePress.
-->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**System Info**
- vitepress version:
- vite version:
- Node version:
- OS version:
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,60 @@
name: "\U0001F41E Bug report"
description: Create a report to help us improve
labels: ['bug: pending triage']
body:
- type: markdown
attributes:
value: |
"Thanks for taking the time to fill out this bug report!
VitePress is still WIP, and it is not compatible with VuePress.
Please do not open issue about default theme missing features or something doesn't work like VuePress."
- type: textarea
id: bug-description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
placeholder: Bug description
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: Steps to reproduce the behavior
placeholder: Reproduction
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
placeholder: Expected behavior
validations:
required: true
- type: textarea
id: system-info
attributes:
label: System Info
description: Output of `npx envinfo --system --npmPackages vitepress --binaries --browsers`
render: shell
placeholder: System, Binaries, Browsers
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Add any other context or screenshots about the bug report here.
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Follow our [Code of Conduct](https://vuejs.org/about/coc.html)
required: true
- label: Read the [docs](https://vitepress.vuejs.org).
required: true
- label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
required: true

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -0,0 +1,45 @@
name: "\U0001F680 New feature proposal"
description: Suggest an idea for this project
body:
- type: markdown
attributes:
value: |
Thanks for your interest in the project and taking the time to fill out this feature report!
- type: textarea
id: feature-description
attributes:
label: Is your feature request related to a problem? Please describe.
description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternative
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
- type: checkboxes
id: checkboxes
attributes:
label: Validations
description: Before submitting the issue, please make sure you do the following
options:
- label: Follow our [Code of Conduct](https://vuejs.org/about/coc.html)
required: true
- label: Read the [docs](https://vitepress.vuejs.org).
required: true
- label: Read the [Contributing Guidelines](https://github.com/vuejs/vitepress/blob/main/.github/contributing.md).
required: true
- label: Check that there isn't already an issue that asks for the same feature to avoid creating a duplicate.
required: true

@ -6,7 +6,7 @@
Messages must be matched by the following regex:
``` js
```js
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip)(\(.+\))?: .{1,50}/
```
@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
### Full Message Format
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
```
<type>(<scope>): <subject>
@ -74,9 +74,9 @@ The scope could be anything specifying the place of the commit change. For examp
The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end
### Body

@ -7,7 +7,7 @@ Hi! We're really excited that you are interested in contributing to VitePress. B
## Pull Request Guidelines
- Checkout a topic branch from the relevant branch, e.g. `master`, and merge back against that branch.
- Checkout a topic branch from the relevant branch, e.g. `main`, and merge back against that branch.
- If adding a new feature:
@ -23,26 +23,27 @@ Hi! We're really excited that you are interested in contributing to VitePress. B
## Development Setup
You will need [Yarn](https://classic.yarnpkg.com/en/docs/cli/install/)/
You will need [pnpm](https://pnpm.io)
After cloning the repo, run:
```bash
$ yarn # install the dependencies of the project
# install the dependencies of the project
$ pnpm install
```
### Setup VitePress Dev Environment
You may start VitePress local dev environment by running `yarn dev`.
You may start VitePress local dev environment by running `pnpm run dev`.
```bash
$ yarn dev
$ pnpm run dev
```
The easiest way to start testing out VitePress is to tweak the VitePress docs. You may run `yarn docs` folder to boot up VitePress documentation site locally, with live reloading of the source code.
The easiest way to start testing out VitePress is to tweak the VitePress docs. You may run `pnpm run docs` folder to boot up VitePress documentation site locally, with live reloading of the source code.
```bash
$ yarn docs
$ pnpm run docs
```
After executing the above command, visit http://localhost:3000 and try modifying the source code. You'll get live update.

@ -0,0 +1,23 @@
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@master
- name: Create Release for Tag
id: release_tag
uses: yyx990803/release-tag@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
body: |
Please refer to [CHANGELOG.md](https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md) for details.

@ -7,14 +7,27 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 15]
node-version: [14, 16]
steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
- name: Checkout
uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2.0.1
with:
version: 6.15.1
- name: Set node version to ${{ matrix.node_version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: install and test
run: |
yarn install
yarn test
node-version: ${{ matrix.node_version }}
cache: 'pnpm'
- name: Install deps
run: pnpm install
- name: Build
run: pnpm run build
- name: Test
run: pnpm run test

8
.gitignore vendored

@ -1,10 +1,12 @@
/coverage
/src/client/shared
/src/node/shared
/src/client/shared.ts
/src/node/shared.ts
*.log
.DS_Store
.vite_opt_cache
dist
node_modules
TODOs.md
.vscode
.vscode
.idea
pnpm-global

@ -1,3 +1,413 @@
## [0.22.3](https://github.com/vuejs/vitepress/compare/v0.22.2...v0.22.3) (2022-02-22)
### Bug Fixes
- append base to links ([#502](https://github.com/vuejs/vitepress/issues/502)) ([804954c](https://github.com/vuejs/vitepress/commit/804954cf4d5417b1abcba9854ed5f064348292c5)), closes [#252](https://github.com/vuejs/vitepress/issues/252)
- avoid minimizing non-javascript inline scripts ([#517](https://github.com/vuejs/vitepress/issues/517)) ([779b789](https://github.com/vuejs/vitepress/commit/779b78902fc7b1f9e7806751c0ca1e229a2161ce)), closes [#538](https://github.com/vuejs/vitepress/issues/538) [#540](https://github.com/vuejs/vitepress/issues/540)
- **client router:** tolerant invalid hash selector typo ([#506](https://github.com/vuejs/vitepress/issues/506)) ([ffe0c40](https://github.com/vuejs/vitepress/commit/ffe0c40ebc42d7769b5378775cdffcab52d3cf11))
- don't add .html to urls of non-html files ([#515](https://github.com/vuejs/vitepress/issues/515)) ([34d1542](https://github.com/vuejs/vitepress/commit/34d1542f466e2eed28b1be7153d1c3461d84528f)), closes [#265](https://github.com/vuejs/vitepress/issues/265)
- normalize relative img src ([#514](https://github.com/vuejs/vitepress/issues/514)) ([9270477](https://github.com/vuejs/vitepress/commit/9270477fa59545978dc2732ac0a8091bed39625f)), closes [#450](https://github.com/vuejs/vitepress/issues/450)
- require at least node v14 ([#546](https://github.com/vuejs/vitepress/issues/546)) ([7cf7011](https://github.com/vuejs/vitepress/commit/7cf70111a5a00579d46453b682ef33169c7846c5))
- reset page data on 404 ([#497](https://github.com/vuejs/vitepress/issues/497)) ([28eaa3b](https://github.com/vuejs/vitepress/commit/28eaa3b04ab71674330151d2a9b79d52c382e71e))
## [0.22.2](https://github.com/vuejs/vitepress/compare/v0.22.1...v0.22.2) (2022-02-14)
### Features
- improve default chunk strategy + page hash stability ([1ef69e2](https://github.com/vuejs/vitepress/commit/1ef69e212f91e43431b4fe4bdba17ca4f29a7b49))
## [0.22.1](https://github.com/vuejs/vitepress/compare/v0.22.0...v0.22.1) (2022-02-14)
### Features
- automatically update hash map + retry on failed page fetch ([2324948](https://github.com/vuejs/vitepress/commit/23249483d60da1952c64a1f764873652b587c2dc))
- use git-based lastUpdated data ([d32d8d4](https://github.com/vuejs/vitepress/commit/d32d8d441917dcb480a6735da78c2d6fc3e589c0))
Note: lastUpdated data is now disabled by default due to the performance overhead of retrieving the git information. This also means each page's metadata object no longer contains the `lastUpdated` property by default - it will only be present if the new `lastUpdated: true` config option is enabled.
# [0.22.0](https://github.com/vuejs/vitepress/compare/v0.21.6...v0.22.0) (2022-02-11)
- Upgrade to Vite 2.8
## [0.21.6](https://github.com/vuejs/vitepress/compare/v0.21.5...v0.21.6) (2022-01-19)
### Perf
- Avoid wrapping siteData as readonly proxy in production builds
## [0.21.5](https://github.com/vuejs/vitepress/compare/v0.21.4...v0.21.5) (2022-01-16)
### Bug Fixes
- allow overriding title if home is true ([#493](https://github.com/vuejs/vitepress/issues/493)) ([88d57a9](https://github.com/vuejs/vitepress/commit/88d57a93ef2689a8f5344b7f38b26db5ea86759b))
- **types:** fix vitepress/theme type ([eabf6d2](https://github.com/vuejs/vitepress/commit/eabf6d2aa69d2a5452042bbb59edbbbc95aece87)), closes [#489](https://github.com/vuejs/vitepress/issues/489) [#438](https://github.com/vuejs/vitepress/issues/438) [#494](https://github.com/vuejs/vitepress/issues/494) [#442](https://github.com/vuejs/vitepress/issues/442)
### Features
- scrollOffset option ([b66785d](https://github.com/vuejs/vitepress/commit/b66785d68a86c118a7a036f3de8b3e504390f1da))
## [0.21.4](https://github.com/vuejs/vitepress/compare/v0.21.3...v0.21.4) (2022-01-07)
### Bug Fixes
- set \_\_data in md.render ([dfbc932](https://github.com/vuejs/vitepress/commit/dfbc932fac50d39b047b211cedca0dcce05aebc8))
## [0.21.3](https://github.com/vuejs/vitepress/compare/v0.21.2...v0.21.3) (2022-01-06)
### Bug Fixes
- prioritize vue installed in user project root ([9b3243b](https://github.com/vuejs/vitepress/commit/9b3243b75752209943af5b247f5d38e641d4ff6d))
## [0.21.2](https://github.com/vuejs/vitepress/compare/v0.21.1...v0.21.2) (2022-01-06)
## [0.21.1](https://github.com/vuejs/vitepress/compare/v0.21.0...v0.21.1) (2022-01-06)
### Performance Improvements
- do not include head config in client bundle for production ([6f3a96f](https://github.com/vuejs/vitepress/commit/6f3a96f06daec4baad4420b54137a7afb1512e7f))
# [0.21.0](https://github.com/vuejs/vitepress/compare/v0.20.10...v0.21.0) (2022-01-06)
### Bug Fixes
- Chinese file link build failed ([#425](https://github.com/vuejs/vitepress/issues/425)) ([ae029ae](https://github.com/vuejs/vitepress/commit/ae029ae9e17fa6df1d2f89043f1891271e9c5b9b)), closes [#424](https://github.com/vuejs/vitepress/issues/424)
- initial render of 404 pages ([#418](https://github.com/vuejs/vitepress/issues/418)) ([a3bf52f](https://github.com/vuejs/vitepress/commit/a3bf52fed53e82b9756c844f6bdd576662d2e726))
- remove `.` for mjs in `supportedConfigExtensions` ([#447](https://github.com/vuejs/vitepress/issues/447)) ([fb6a4ad](https://github.com/vuejs/vitepress/commit/fb6a4ad3e008af9ce4393fb3ca37645f4efba951))
- **serve:** respect base config in serve mode ([#470](https://github.com/vuejs/vitepress/issues/470)) ([08a0b12](https://github.com/vuejs/vitepress/commit/08a0b129928cef44e613ff410d769a7ac7bf5fa3)), closes [#416](https://github.com/vuejs/vitepress/issues/416)
- set tempDir outside package root ([#439](https://github.com/vuejs/vitepress/issues/439)) ([bd35451](https://github.com/vuejs/vitepress/commit/bd35451ed42d7b5c47e2b49a7e659807cd7d7a0c)), closes [#435](https://github.com/vuejs/vitepress/issues/435)
- use algolia search lang ([#459](https://github.com/vuejs/vitepress/issues/459)) ([444562c](https://github.com/vuejs/vitepress/commit/444562c3a763bab7a9c0ebfca5eec635e142a61f))
### Features
- add details custom container ([#455](https://github.com/vuejs/vitepress/issues/455)) ([a8f147f](https://github.com/vuejs/vitepress/commit/a8f147f153efdd17989a02eb620c3ae9ab0d13dd))
- catch localhost links as dead links ([7387649](https://github.com/vuejs/vitepress/commit/7387649ff7c621402e49e26493b4eed25006fb4b))
- expose `__path` and `__relativePath` on md instance for md plugins ([4cec660](https://github.com/vuejs/vitepress/commit/4cec660401d8d01830e5a11b9c66bc0ac5a935db))
- improve typescript support for config file, add `defineConfigWithTheme` ([#465](https://github.com/vuejs/vitepress/issues/465)) ([ba41bb9](https://github.com/vuejs/vitepress/commit/ba41bb90551c01b9f84de2d2d3bc1920ce2ebe93))
- properly remove `{#custom-anchor}` syntax in headers ([6120da2](https://github.com/vuejs/vitepress/commit/6120da25a87f6bec3918be804e95f2b3c8afb6c8))
- user configurable `outDir` ([#448](https://github.com/vuejs/vitepress/issues/448)) ([5b04bb9](https://github.com/vuejs/vitepress/commit/5b04bb9eb5ced720414f4b0d729fde36432dd451))
## [0.20.10](https://github.com/vuejs/vitepress/compare/v0.20.9...v0.20.10) (2021-12-25)
### Features
- minify head inline scripts ([e61db62](https://github.com/vuejs/vitepress/commit/e61db62a1c49cb5f368a152221bfa60737dbbc6a))
## [0.20.9](https://github.com/vuejs/vitepress/compare/v0.20.8...v0.20.9) (2021-12-15)
### Features
- shouldPreload hook ([e721d60](https://github.com/vuejs/vitepress/commit/e721d605851be4e27f4948d96d5c3bab6d23ead2))
- support array of patterns in data loaders ([f5308d7](https://github.com/vuejs/vitepress/commit/f5308d746f3089ef6818b0139fe249827a47628b))
## [0.20.8](https://github.com/vuejs/vitepress/compare/v0.20.7...v0.20.8) (2021-12-14)
## [0.20.7](https://github.com/vuejs/vitepress/compare/v0.20.6...v0.20.7) (2021-12-14)
### Features
- **types:** re-export vite client type ([4caa7b2](https://github.com/vuejs/vitepress/commit/4caa7b231753ddedb83365a37b8c259ae461bd37))
## [0.20.6](https://github.com/vuejs/vitepress/compare/v0.20.4...v0.20.6) (2021-12-14)
### Features
- support static data loaders ([26fe81c](https://github.com/vuejs/vitepress/commit/26fe81c88618d7df5d623d041ac3df96e7d7ee7b))
## [0.20.5](https://github.com/vuejs/vitepress/compare/v0.20.4...v0.20.5) (2021-12-12)
- Bump vue & vite versions
## [0.20.4](https://github.com/vuejs/vitepress/compare/v0.20.3...v0.20.4) (2021-12-07)
### Bug Fixes
- **build:** fix typing files ([ae11dc0](https://github.com/vuejs/vitepress/commit/ae11dc0b59ac90375079f1ebf0efacf1b1e58e8d))
## [0.20.3](https://github.com/vuejs/vitepress/compare/v0.20.2...v0.20.3) (2021-12-07)
### Features
- expose createMarkdownRenderer ([d54c7d8](https://github.com/vuejs/vitepress/commit/d54c7d8c56973dac138bfe96ff16dfab162ef64b))
## [0.20.2](https://github.com/vuejs/vitepress/compare/v0.20.1...v0.20.2) (2021-12-06)
### Bug Fixes
- handle potential string quote mismatch in generated code ([dfa7c05](https://github.com/vuejs/vitepress/commit/dfa7c0525f010994437acb060867d9ca1572867d))
- improve createStaticVNode match for rollup codegen compat ([abb1b57](https://github.com/vuejs/vitepress/commit/abb1b578cdedf184ae386ce455e60a23672adfcb))
- lazy require @vitejs/plugin-vue to respect NODE_ENV ([a051e66](https://github.com/vuejs/vitepress/commit/a051e66f1ae211174cf470d4430427dc0189194b))
- static string strip regex for mulitiline static strings ([bc486aa](https://github.com/vuejs/vitepress/commit/bc486aae563fd77f38da44d9ae3ea28c021f6df0))
### Features
- upgrade docsearch version ([#441](https://github.com/vuejs/vitepress/issues/441)) ([1b245e2](https://github.com/vuejs/vitepress/commit/1b245e22d8a00ea7c01c052ac1ea3d8d94aaeefb))
## [0.20.1](https://github.com/vuejs/vitepress/compare/v0.20.0...v0.20.1) (2021-11-05)
### Bug Fixes
- **hmr:** avoid relying on revertd vite hmr behavior ([4114674](https://github.com/vuejs/vitepress/commit/4114674c69f917ff2e611ec30eb72d224f175f62))
# [0.20.0](https://github.com/vuejs/vitepress/compare/v0.19.2...v0.20.0) (2021-10-07)
### Bug Fixes
- fix code line hightlighting ([4c042b6](https://github.com/vuejs/vitepress/commit/4c042b61e7beb70d0a0b77cc9a00d725c7863089)), closes [#408](https://github.com/vuejs/vitepress/issues/408)
- invalid active props when `base` option is added ([#342](https://github.com/vuejs/vitepress/issues/342)) ([383d8ff](https://github.com/vuejs/vitepress/commit/383d8ffbba5283774e0f1e39302a29efc0db7e79))
- make config hmr work in window ([#364](https://github.com/vuejs/vitepress/issues/364)) ([58663bb](https://github.com/vuejs/vitepress/commit/58663bbd02aa3da0efd939bd27de2ee5c0ab14d8))
- print urls again ([df69b76](https://github.com/vuejs/vitepress/commit/df69b76427ab2c770010cd79e1076a1c414fb3bc))
- support vite plugins provided via `config.vite` ([#394](https://github.com/vuejs/vitepress/issues/394)) ([4b76617](https://github.com/vuejs/vitepress/commit/4b7661762143b033e82fad526e256f7bc54df9af))
- **theme-default/algolia:** avoid creating multiple algolia searches ([#292](https://github.com/vuejs/vitepress/issues/292)) ([389e863](https://github.com/vuejs/vitepress/commit/389e863b4d5e69c856d1e647d4d4c1807bd94c5d))
- **theme:** fix algolia search filter ([5fd7db2](https://github.com/vuejs/vitepress/commit/5fd7db2b7fcd947d77c97b1e9bdaf83845c1321d))
- tolerant invalid hash ([#399](https://github.com/vuejs/vitepress/issues/399)) ([efc5e1b](https://github.com/vuejs/vitepress/commit/efc5e1b2566eedc47a9420accae3dfba1a594ba4))
### Features
- support ts/esm config file + defineConfig() helper ([d3b1521](https://github.com/vuejs/vitepress/commit/d3b1521ebef831e0d0307b3b12e4fc1f6ce4721a)), closes [#339](https://github.com/vuejs/vitepress/issues/339) [#376](https://github.com/vuejs/vitepress/issues/376)
- **theme-default:** home slot for customizing the entire homepage easily ([#314](https://github.com/vuejs/vitepress/issues/314)) ([07bf145](https://github.com/vuejs/vitepress/commit/07bf1451909ad615565e01d719e8a350ea07e69e))
## [0.19.2](https://github.com/vuejs/vitepress/compare/v0.19.1...v0.19.2) (2021-09-28)
### Bug Fixes
- encode urls that conflict w/ vite built-in replacements ([3940625](https://github.com/vuejs/vitepress/commit/3940625121455b7ad6e5ea8ebb3e1cf2faf9c7fc))
## [0.19.1](https://github.com/vuejs/vitepress/compare/v0.19.0...v0.19.1) (2021-09-21)
- Fix build
# [0.19.0](https://github.com/vuejs/vitepress/compare/v0.18.1...v0.19.0) (2021-09-21)
### Features
- upgrade vue, simplify deps ([9030486](https://github.com/vuejs/vitepress/commit/9030486409f10a59115d874b9365f71348ed76c2))
- use `markdown-it-attrs` for markdown-it plugins ([#393](https://github.com/vuejs/vitepress/issues/393)) ([610e9b7](https://github.com/vuejs/vitepress/commit/610e9b7111462d3aace878017fa4d359cd2ae7ea))
## [0.18.1](https://github.com/vuejs/vitepress/compare/v0.18.0...v0.18.1) (2021-09-16)
### Bug Fixes
- ensure stable pages entry order across builds ([929bcf5](https://github.com/vuejs/vitepress/commit/929bcf50ee634d9fe73adbe2aae5f7038b048e5a))
# [0.18.0](https://github.com/vuejs/vitepress/compare/v0.17.3...v0.18.0) (2021-09-14)
### Features
- map mode + remove deprecated options ([b94b163](https://github.com/vuejs/vitepress/commit/b94b163a3a931fe03e69547391d6ac22eb41b789))
- support `<script client>` in mpa mode ([e0b6997](https://github.com/vuejs/vitepress/commit/e0b69973f840bfa281fae209da1f1c674c1301a8))
## [0.17.3](https://github.com/vuejs/vitepress/compare/v0.17.2...v0.17.3) (2021-09-09)
### Bug Fixes
- emit prevented hashchange event ([4fb387d](https://github.com/vuejs/vitepress/commit/4fb387d94ea9d7ae28a871929cbbc57e931b8d7a))
## [0.17.2](https://github.com/vuejs/vitepress/compare/v0.17.1...v0.17.2) (2021-09-08)
### Bug Fixes
- improve fs allow ([2e9264f](https://github.com/vuejs/vitepress/commit/2e9264f03259354e7739e2a56a7c1306fb167843))
### Features
- support config.extends ([f749b27](https://github.com/vuejs/vitepress/commit/f749b272d4603a3b8eaf251b0feebe2d33da3983))
## [0.17.1](https://github.com/vuejs/vitepress/compare/v0.17.0...v0.17.1) (2021-09-08)
### Bug Fixes
- avoid using spread for client code ([03abee7](https://github.com/vuejs/vitepress/commit/03abee7f7c0fac95806f31ff5761b9e912a1f232))
- **default-theme:** use description as tagline by default ([b94c827](https://github.com/vuejs/vitepress/commit/b94c82710a7b230a918790ac0b6aa1d2f5afc1c3))
- handle case when there is no themeConfig ([034c737](https://github.com/vuejs/vitepress/commit/034c7375ad2de4b42c0ac861c2dd18183511771d))
### Performance Improvements
- minor optimizations ([96bcdda](https://github.com/vuejs/vitepress/commit/96bcddabedac9af4e1c817ed651bb4ce692c75e7))
# [0.17.0](https://github.com/vuejs/vitepress/compare/v0.16.1...v0.17.0) (2021-08-31)
### Bug Fixes
- allow vite server access to theme and local files ([9b9fdc7](https://github.com/vuejs/vitepress/commit/9b9fdc710a6cedb3e278805eb07bed669ca2075e))
- **code:** code block highlight bug in ul ([#352](https://github.com/vuejs/vitepress/issues/352)) ([9245226](https://github.com/vuejs/vitepress/commit/9245226b16f6113c722e5e8c7b876bea1cf1c255))
- **css:** remove 720px breakpoint in home layout ([#347](https://github.com/vuejs/vitepress/issues/347)) ([0c1a1f2](https://github.com/vuejs/vitepress/commit/0c1a1f2ef43cd7d995f3e9d43f19be8b3f961cb1))
- **i18n:** fix locales reading, add site.langs ([#353](https://github.com/vuejs/vitepress/issues/353)) ([bc78adb](https://github.com/vuejs/vitepress/commit/bc78adb468bce8ce2d4e2543423adacc9351cf51)), closes [/vuepress.vuejs.org/guide/i18n.html#site-level-i18](https://github.com//vuepress.vuejs.org/guide/i18n.html/issues/site-level-i18) [/v2.vuepress.vuejs.org/guide/i18n.html#site-i18](https://github.com//v2.vuepress.vuejs.org/guide/i18n.html/issues/site-i18)
- include emoji text in nav link to match toc ([#284](https://github.com/vuejs/vitepress/issues/284)) ([80ff360](https://github.com/vuejs/vitepress/commit/80ff36066ef6a4ed4a18548993bc5d8d9a6dab58))
- use useData() instead of $site ([#365](https://github.com/vuejs/vitepress/issues/365)) ([1e64773](https://github.com/vuejs/vitepress/commit/1e6477393308a5d8bd03a614cecf9573466f6e6c))
### Features
- support function config ([e74c5f0](https://github.com/vuejs/vitepress/commit/e74c5f06d1d5890fad6dd728df9bf85dcfda87d1))
- support partial include directive ([7b3a9e5](https://github.com/vuejs/vitepress/commit/7b3a9e59b44e9e354692eed6c1ca453be9cb7a86))
- upgrade markdown-it-anchor ([#350](https://github.com/vuejs/vitepress/issues/350)) ([26b5aa9](https://github.com/vuejs/vitepress/commit/26b5aa931f1935bd67dcd1d511461ff5fa8a00ec))
### BREAKING CHANGES
- the `markdown.anchor` option is updated. Refer to
valeriangalliat/markdown-it-anchor#permalinks for
instructions to upgrade your existing `markdown.anchor.permalink`
option. **This doesn't affect you if you weren't changing the header
permalinks behavior**.
## [0.16.1](https://github.com/vuejs/vitepress/compare/v0.16.0...v0.16.1) (2021-08-11)
### Features
- info custom container ([4925fb5](https://github.com/vuejs/vitepress/commit/4925fb5c29c59b7e17d050ab4346f71afc0463cd))
# [0.16.0](https://github.com/vuejs/vitepress/compare/v0.15.6...v0.16.0) (2021-08-10)
This version uses Vue 3.2.0.
### Bug Fixes
- override target and rel links attribute in config ([#332](https://github.com/vuejs/vitepress/issues/332)) ([9d98dbb](https://github.com/vuejs/vitepress/commit/9d98dbbe60d477a78d6dc0e80d16fdddedcd4ed5))
- **edit-link:** let frontmatter overwrite global editLink ([#340](https://github.com/vuejs/vitepress/issues/340)) ([cfbba80](https://github.com/vuejs/vitepress/commit/cfbba80a0a6e33bcb2ca3d4450fb9624dcd6d140))
## [0.15.6](https://github.com/vuejs/vitepress/compare/v0.15.5...v0.15.6) (2021-07-02)
### Bug Fixes
- automatically escape vite user defined variables in markdown ([3cec536](https://github.com/vuejs/vitepress/commit/3cec536c1f3d5d027ee16cd0629f84461e565096))
- skip external URLs in `withBase` ([#328](https://github.com/vuejs/vitepress/issues/328)) ([53bb961](https://github.com/vuejs/vitepress/commit/53bb961a925cbafe53730450c5b069e255b54e03))
## [0.15.5](https://github.com/vuejs/vitepress/compare/v0.15.4...v0.15.5) (2021-06-23)
### Bug Fixes
- **nav:** display nav if locales are present ([#321](https://github.com/vuejs/vitepress/issues/321)) ([e76e6ec](https://github.com/vuejs/vitepress/commit/e76e6ecd54f8a202a9d5051afd72553080f898c9))
- **search:** correctly detect multilang ([c046905](https://github.com/vuejs/vitepress/commit/c046905b032a765352ff6bb9944f72db76c5cf45)), closes [#316](https://github.com/vuejs/vitepress/issues/316)
### Performance Improvements
- only update necessary head tags in prod ([e6bb5a4](https://github.com/vuejs/vitepress/commit/e6bb5a4806bb16f1ace26f27f43b5ed83885bf1a))
## [0.15.4](https://github.com/vuejs/vitepress/compare/v0.15.3...v0.15.4) (2021-06-19)
### Bug Fixes
- avoid scroll behavior reliance on .nav-bar class ([9b35dfc](https://github.com/vuejs/vitepress/commit/9b35dfcde4c00e6f10b2631103f95e97cbf4af9e))
## [0.15.3](https://github.com/vuejs/vitepress/compare/v0.15.2...v0.15.3) (2021-06-15)
### Bug Fixes
- avoid error when theme does not have .nav-bar class ([a9d5800](https://github.com/vuejs/vitepress/commit/a9d580069fff90298b197808379cd0c4b756d463))
- avoid resetting head tags on hmr/page switch ([f52f20e](https://github.com/vuejs/vitepress/commit/f52f20e02f6908411ad9cddb341583456a3c2a8c))
- watch config file when using srcDir ([348f19a](https://github.com/vuejs/vitepress/commit/348f19a537930b1d4c7272e05e91edcb72219f34))
## [0.15.2](https://github.com/vuejs/vitepress/compare/v0.15.1...v0.15.2) (2021-06-15)
### Bug Fixes
- force optimize vue to avoid duplication when linked ([eefba39](https://github.com/vuejs/vitepress/commit/eefba398b0e5a4b5afb47ce6e06b0c39a6be55d2))
## [0.15.1](https://github.com/vuejs/vitepress/compare/v0.15.0...v0.15.1) (2021-06-14)
### Features
- support passing vite config in vitepress config file via `vite` option ([3737b10](https://github.com/vuejs/vitepress/commit/3737b1055dc1145dc70b10994564c6d83affd15d))
- support srcDir config option ([aaf4910](https://github.com/vuejs/vitepress/commit/aaf4910d938f4449fdab576ffd0ae853b5aace24))
### Performance Improvements
- avoid double resolve user config on startup ([5733fc6](https://github.com/vuejs/vitepress/commit/5733fc625ea33ab1b07ddfd4f8412e15473d8cca))
### BREAKING CHANGES
- Some config options have changed.
- `vueOptions` renamed to `vue`
- `alias` option has been removed. Use `vite.resovle.alias` instead.
# [0.15.0](https://github.com/vuejs/vitepress/compare/v0.14.1...v0.15.0) (2021-06-14)
### Bug Fixes
- fix frontmatter sidebarDepth for headers ([424a4ca](https://github.com/vuejs/vitepress/commit/424a4ca379f028e3542e2e9598cb5beacaf50067))
- fix vue code block type indication ([76fa173](https://github.com/vuejs/vitepress/commit/76fa1733fff4e3aa4356df08272e4811db996dab))
### Features
- more efficient `useData()` method that exposes all data ([0661063](https://github.com/vuejs/vitepress/commit/0661063d29c0e1dce108cac608be0ff754d2d4c1))
### BREAKING CHANGES
- The following methods are removed.
- `useSiteData`
- `useSiteDataByRoute`
- `usePageData`
- `useFrontmatter`
Instead, use the new `useData()` method:
```js
// before
import { useSiteDataByRoute, usePageData } from 'vitepress'
const site = useSiteDataByRoute()
const page = usePageData()
const theme = computed(() => site.value.themeConfig)
// after
import { useData } from 'vitepress'
const { site, page, theme } = useData()
```
All destructured values are computed refs injected from app root
so they are created only once globally.
- All global mixin properties (e.g. `$site`) except `$frontmatter` are removed. Always use `useData()` to retrieve VitePress data in Vue components.
## [0.14.1](https://github.com/vuejs/vitepress/compare/v0.14.0...v0.14.1) (2021-06-08)
### Bug Fixes
- functional templates with vue v3.1 ([#312](https://github.com/vuejs/vitepress/issues/312)) ([8988aad](https://github.com/vuejs/vitepress/commit/8988aadbcbd781a81df0a8d1a4a6964d324c58a3))
# [0.14.0](https://github.com/vuejs/vitepress/compare/v0.13.2...v0.14.0) (2021-05-27)
### Bug Fixes
- chinese filenames can't build ([#217](https://github.com/vuejs/vitepress/issues/217)) ([#262](https://github.com/vuejs/vitepress/issues/262)) ([b940397](https://github.com/vuejs/vitepress/commit/b940397cd0e5135e7433bac6fc99da8553915053))
- **theme:** set search box min-width for >=751px ([#286](https://github.com/vuejs/vitepress/issues/286)) ([9589a5d](https://github.com/vuejs/vitepress/commit/9589a5d0e6458da07054a84d2df6ef99a5ad1dbd))
- detect public folder for dead link ([#290](https://github.com/vuejs/vitepress/issues/290)) ([3aa185f](https://github.com/vuejs/vitepress/commit/3aa185fa9f9dd49e32cfd60f96a30da8616e419e))
- remove unnecessary 'vite/dynamic-import-polyfill' ([6b4a4aa](https://github.com/vuejs/vitepress/commit/6b4a4aa7a6cd2f2e044a5cc54c5bf72a50f9df67))
### Features
- Vite version bumped to `^2.3.4`
- exclude option ([#281](https://github.com/vuejs/vitepress/issues/281)) ([71a5e1c](https://github.com/vuejs/vitepress/commit/71a5e1c2a2b552ced8a994dc60201c4be89b4ac9))
- Render titles for social sharing and improve home page sharing ([#263](https://github.com/vuejs/vitepress/issues/263)) ([e651f97](https://github.com/vuejs/vitepress/commit/e651f977d6b9f50fef25f6d736f8d4880c997305))
## [0.13.2](https://github.com/vuejs/vitepress/compare/v0.13.1...v0.13.2) (2021-04-26)
### Bug Fixes
- **search:** silence warning for prop ([0716ffa](https://github.com/vuejs/vitepress/commit/0716ffade743c65c240d616329c1a6bc3e83c4bd))
## [0.13.1](https://github.com/vuejs/vitepress/compare/v0.13.0...v0.13.1) (2021-04-26)
### Bug Fixes
- **locales:** use correct lang ([#283](https://github.com/vuejs/vitepress/issues/283)) ([de89c1e](https://github.com/vuejs/vitepress/commit/de89c1e5ebf09557532eec93269b7143f454f9d1))
# [0.13.0](https://github.com/vuejs/vitepress/compare/v0.12.2...v0.13.0) (2021-04-08)
### Bug Fixes
- build fails without css chunks ([#209](https://github.com/vuejs/vitepress/issues/209)) ([#239](https://github.com/vuejs/vitepress/issues/239)) ([fa469fd](https://github.com/vuejs/vitepress/commit/fa469fd2750ac74417238de7547cd8e7cd939cb0))
- **css:** reuse css vars ([#256](https://github.com/vuejs/vitepress/issues/256)) ([8d91524](https://github.com/vuejs/vitepress/commit/8d915245c6740874abad0e11f374703aa07afec3))
- **docs:** global-component link ([#271](https://github.com/vuejs/vitepress/issues/271)) ([a43933c](https://github.com/vuejs/vitepress/commit/a43933c8ab4474c905005a337da3620621878a1c))
- **locales:** use correct lang ([#276](https://github.com/vuejs/vitepress/issues/276)) ([f505db9](https://github.com/vuejs/vitepress/commit/f505db945af3ca4e4ce0a06b5aa2a4d32d47bac7))
- **navbar:** use css var for background-color ([#264](https://github.com/vuejs/vitepress/issues/264)) ([f385bc4](https://github.com/vuejs/vitepress/commit/f385bc467306c075871485e6a9bfe773fc9054a1))
- badge for language-javascript ([#245](https://github.com/vuejs/vitepress/issues/245)) ([f8b4aa5](https://github.com/vuejs/vitepress/commit/f8b4aa5baa7f2fba843427f2f4f3985222c3c78d))
### Features
- detect dead links ([74f5ada](https://github.com/vuejs/vitepress/commit/74f5adafcde8a597939fbb37422aa68187b6dad4))
- import code snippet with region ([#237](https://github.com/vuejs/vitepress/issues/237)) ([#238](https://github.com/vuejs/vitepress/issues/238)) ([d1a62e1](https://github.com/vuejs/vitepress/commit/d1a62e1c6630b289777b78eea359acd49174148d))
## [0.12.2](https://github.com/vuejs/vitepress/compare/v0.12.1...v0.12.2) (2021-02-15)
### Bug Fixes

@ -5,11 +5,7 @@
---
**:fire: Note this is early WIP! Currently the focus is on making Vite stable and feature complete first. It is not recommended to use this for anything serious yet.**
---
VitePress is [VuePress](http://vuepress.vuejs.org/)' little brother, built on top of [vite](https://github.com/vuejs/vite).
VitePress is [VuePress](https://vuepress.vuejs.org)' spiritual successor, built on top of [vite](https://github.com/vitejs/vite).
## Documentation
@ -17,14 +13,14 @@ To check out docs, visit [vitepress.vuejs.org](https://vitepress.vuejs.org).
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vitepress/releases).
Detailed changes for each release are documented in the [CHANGELOG](https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md).
## Contribution
Please make sure to read the [Contributing Guide](./.github/contributing.md) before making a pull request.
Please make sure to read the [Contributing Guide](https://github.com/vuejs/vitepress/blob/main/.github/contributing.md) before making a pull request.
## License
[MIT](https://opensource.org/licenses/MIT)
[MIT](https://github.com/vuejs/vitepress/blob/main/LICENSE)
Copyright (c) 2019-present, Yuxi (Evan) You

@ -0,0 +1,18 @@
{
"compilerOptions": {
"baseUrl": ".",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"noUnusedLocals": true,
"skipLibCheck": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"types": ["node", "vitest/global"],
"paths": {
"node/*": ["../src/node/*"],
"client/*": ["../src/client/*"]
}
},
"include": ["../src", "."]
}

@ -0,0 +1,10 @@
import path from 'path'
export default {
resolve: {
alias: {
node: path.resolve(__dirname, '../src/node'),
client: path.resolve(__dirname, '../src/client')
}
}
}

@ -0,0 +1,53 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"projectFolder": "./src/client",
"mainEntryPointFilePath": "./dist/temp/index.d.ts",
"dtsRollup": {
"enabled": true,
"publicTrimmedFilePath": "./dist/client/index.d.ts"
},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": false
},
"tsdocMetadata": {
"enabled": false
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
},
"extractorMessageReporting": {
"default": {
"logLevel": "warning",
"addToApiReportFile": true
},
"ae-missing-release-tag": {
"logLevel": "none"
}
},
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
},
"tsdoc-undefined-tag": {
"logLevel": "none"
}
}
}
}

@ -0,0 +1,53 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"projectFolder": "./src/node",
"mainEntryPointFilePath": "./dist/temp/index.d.ts",
"dtsRollup": {
"enabled": true,
"publicTrimmedFilePath": "./dist/node/index.d.ts"
},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": false
},
"tsdocMetadata": {
"enabled": false
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
},
"extractorMessageReporting": {
"default": {
"logLevel": "warning",
"addToApiReportFile": true
},
"ae-missing-release-tag": {
"logLevel": "none"
}
},
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
},
"tsdoc-undefined-tag": {
"logLevel": "none"
}
}
}
}

4
client.d.ts vendored

@ -0,0 +1,4 @@
// re-export vite client types
// with strict installers like pnpm, user won't be able to reference vite/client
// in project root
/// <reference types="vite/client" />

@ -1,18 +1,22 @@
module.exports = {
import { defineConfig } from '../../src/node'
export default defineConfig({
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
lastUpdated: true,
themeConfig: {
repo: 'vuejs/vitepress',
docsDir: 'docs',
docsBranch: 'main',
editLinks: true,
editLinkText: 'Edit this page on GitHub',
lastUpdated: 'Last Updated',
algolia: {
apiKey: 'c57105e511faa5558547599f120ceeba',
appId: '8J64VVRP8K',
apiKey: 'a18e2f4cc5665f6602c5631fd868adfd',
indexName: 'vitepress'
},
@ -41,7 +45,7 @@ module.exports = {
'/': getGuideSidebar()
}
}
}
})
function getGuideSidebar() {
return [
@ -61,9 +65,8 @@ function getGuideSidebar() {
text: 'Advanced',
children: [
{ text: 'Frontmatter', link: '/guide/frontmatter' },
{ text: 'Global Computed', link: '/guide/global-computed' },
{ text: 'Global Component', link: '/guide/global-component' },
{ text: 'Customization', link: '/guide/customization' },
{ text: 'Theming', link: '/guide/theming' },
{ text: 'API Reference', link: '/guide/api' },
{
text: 'Differences from Vuepress',
link: '/guide/differences-from-vuepress'

@ -1,3 +1,3 @@
<template>
<span>&#x26A1;</span>
</template>
</template>

@ -1,6 +1,6 @@
# Theme Config: Algolia Search
The `themeConfig.algolia` option allows you to use [Algolia DocSearch](https://docsearch.algolia.com/). To enable it, you need to provide at least apiKey and indexName:
The `themeConfig.algolia` option allows you to use [Algolia DocSearch](https://docsearch.algolia.com). To enable it, you need to provide at least apiKey and indexName:
```js
module.exports = {
@ -13,4 +13,38 @@ module.exports = {
}
```
For more options, check out [Algolia DocSearch's documentation](https://github.com/algolia/docsearch#docsearch-options).
For more options, check out [Algolia DocSearch's documentation](https://docsearch.algolia.com/docs/api/). You can pass any extra option alongside other options, e.g. passing `searchParameters`:
```js
module.exports = {
themeConfig: {
algolia: {
apiKey: 'your_api_key',
indexName: 'index_name',
searchParameters: {
facetFilters: ['tags:guide,api']
}
}
}
}
```
## Internationalization (i18n)
If you have multiple locales in your documentation and you have defined a `locales` object in your `themeConfig`:
```js
module.exports = {
themeConfig: {
locales: {
// ...
},
algolia: {
apiKey: 'your_api_key',
indexName: 'index_name'
}
}
}
```
VitePress will automatically add a `lang` _facetFilter_ to the `searchParameters.facetFilter` array with the correct language value. Algolia automatically adds the correct facet filter based on the `lang` attribute on the `<html>` tag. This will match search results with the currently viewed language of the page.

@ -1,5 +1,9 @@
# App Config: Basics
::: tip
The config reference is incomplete since the config format may still receive further changes. For a complete reference of the current available options, refer to [config.ts](https://github.com/vuejs/vitepress/blob/45b65ce8b63bd54f345bfc3383eb2416b6769dc9/src/node/config.ts#L30-L65).
:::
## base
- Type: `string`
@ -22,8 +26,6 @@ module.exports = {
The `lang` attribute for the site. This will render as a `<html lang="en-US">` tag in the page HTML.
Note that the `lang` attribute will only be added when building the site via `vitepress build`. You will not see this rendered during `vitepress dev`.
```js
module.exports = {
lang: 'en-US'
@ -35,7 +37,7 @@ module.exports = {
- Type: `string`
- Default: `VitePress`
Title for the site. This will be the prefix for all page titles, and displayed in the navbar.
Title for the site. This will be the suffix for all page titles, and displayed in the navbar.
```js
module.exports = {

@ -20,5 +20,4 @@ features:
details: VitePress generates pre-rendered static HTML for each page, and runs as an SPA once a page is loaded.
footer: MIT Licensed | Copyright © 2019-present Evan You
---
```

@ -0,0 +1,95 @@
# API Reference
## Helper Methods
The following methods are globally importable from `vitepress` and are typically used in custom theme Vue components. However, they are also usable inside `.md` pages because markdown files are compiled into Vue single-file components.
Methods that start with `use*` indicates that it is a [Vue 3 Composition API](https://vuejs.org/guide/introduction.html#composition-api) function that can only be used inside `setup()` or `<script setup>`.
### `useData`
Returns page-specific data. The returned object has the following type:
```ts
interface VitePressData {
site: Ref<SiteData>
page: Ref<PageData>
theme: Ref<any> // themeConfig from .vitepress/config.js
frontmatter: Ref<PageData['frontmatter']>
title: Ref<string>
description: Ref<string>
lang: Ref<string>
localePath: Ref<string>
}
```
**Example:**
```vue
<script setup>
import { useData } from 'vitepress'
const { theme } = useData()
</script>
<template>
<h1>{{ theme.heroText }}</h1>
</template>
```
### `useRoute`
Returns the current route object with the following type:
```ts
interface Route {
path: string
data: PageData
component: Component | null
}
```
### `useRouter`
Returns the VitePress router instance so you can programmatically navigate to another page.
```ts
interface Router {
route: Route
go: (href?: string) => Promise<void>
}
```
### `withBase`
- **Type**: `(path: string) => string`
Appends the configured [`base`](../config/basics#base) to a given URL path. Also see [Base URL](./assets#base-url).
## Global Components
VitePress comes with few built-in component that can be used globally. You may use these components in your markdown or your custom theme configuration.
### `<Content/>`
The `<Content/>` component displays the rendered markdown contents. Useful [when creating your own theme](./theming).
```vue
<template>
<h1>Custom Layout!</h1>
<Content />
</template>
```
### `<ClientOnly/>`
The `<ClientOnly/>` component renders its slot only at client side.
Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the universal code requirements. In short, make sure to only access Browser / DOM APIs in beforeMount or mounted hooks.
If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the `ClientOnly` component.
```html
<ClientOnly>
<NonSSRFriendlyComponent />
</ClientOnly>
```

@ -26,10 +26,30 @@ Note that you should reference files placed in `public` using root absolute path
If your site is deployed to a non-root URL, you will need to set the `base` option in `.vitepress/config.js`. For example, if you plan to deploy your site to `https://foo.github.io/bar/`, then `base` should be set to `'/bar/'` (it should always start and end with a slash).
With a base URL, to reference an image in `public`, you'd have to use URLs like `/bar/image.png`. But this is brittle if you ever decide to change the base. To help with that, VitePress provides a built-in helper `$withBase` (injected onto Vue's prototype) that generates the correct path:
All your static asset paths are automatically processed to adjust for different `base` config values. For example, if you have an absolute reference to an asset under `public` in your markdown:
```html
<img :src="$withBase('/foo.png')" alt="foo" />
```md
![An image](/image-inside-public.png)
```
You do **not** need to update it when you change the `base` config value in this case.
However, if you are authoring a theme component that links to assets dynamically, e.g. an image whose `src` is based on a theme config value:
```vue
<img :src="theme.logoPath" />
```
Note you can use the above syntax not only in theme components, but in your Markdown files as well.
In this case it is recommended to wrap the path with the [`withBase` helper](./api#withbase) provided by VitePress:
```vue
<script setup>
import { withBase, useData } from 'vitepress'
const { theme } = useData()
</script>
<template>
<img :src="withBase(theme.logoPath)" />
</template>
```

@ -1,5 +1,7 @@
# Configuration
## Overview
Without any configuration, the page is pretty minimal, and the user has no way to navigate around the site. To customize your site, lets first create a `.vitepress` directory inside your docs directory. This is where all VitePress-specific files will be placed. Your project structure is probably like this:
```bash
@ -20,4 +22,58 @@ module.exports = {
}
```
Check out the [Config Reference](/config/basics) for a full list of options.
Check out the [Config Reference](../config/basics) for a full list of options.
## Config Intellisense
Since VitePress ships with TypeScript typings, you can leverage your IDE's intellisense with jsdoc type hints:
```js
/**
* @type {import('vitepress').UserConfig}
*/
const config = {
// ...
}
export default config
```
Alternatively, you can use the `defineConfig` helper at which should provide intellisense without the need for jsdoc annotations:
```js
import { defineConfig } from 'vitepress'
export default defineConfig({
// ...
})
```
VitePress also directly supports TS config files. You can use `.vitepress/config.ts` with the `defineConfig` helper as well.
## Typed Theme Config
By default, `defineConfig` helper leverages the theme config type from default theme:
```ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
// Type is `DefaultTheme.Config`
}
})
```
If you use a custom theme and want type checks for the theme config, you'll need to use `defineConfigWithTheme` instead, and pass the config type for your custom theme via a generic argument:
```ts
import { defineConfigWithTheme } from 'vitepress'
import { ThemeConfig } from 'your-theme'
export default defineConfigWithTheme<ThemeConfig>({
themeConfig: {
// Type is `ThemeConfig`
}
})
```

@ -1,51 +0,0 @@
# Customization
You can develop your custom theme by adding the `.vitepress/theme/index.js` file.
```bash
.
├─ docs
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js
│ │ └─ config.js
│ └─ index.md
└─ package.json
```
In `.vitepress/theme/index.js`, you must export theme object and register your own theme layout. Let's say you have your own `Layout` component like this.
```vue
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Custom Layout!</h1>
<Content /><!-- make sure to include markdown outlet -->
</template>
```
Then include it in `.vitepress/theme/index.js`.
```js
// .vitepress/theme/index.js
import Layout from './Layout.vue'
export default {
Layout,
NotFound: () => 'custom 404', // <- this is a Vue 3 functional component
enhanceApp({ app, router, siteData }) {
// app is the Vue 3 app instance from `createApp()`. router is VitePress'
// custom router. `siteData`` is a `ref`` of current site-level metadata.
}
}
```
If you want to extend the default theme, you can import it from `vitepress/theme`.
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme
}
```

@ -1,3 +1,7 @@
---
sidebarDepth: 3
---
# Deploying
The following guides are based on some shared assumptions:
@ -34,7 +38,7 @@ $ yarn docs:build
$ yarn docs:serve
```
The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at http://localhost:5000. It's an easy way to check if the production build looks OK in your local environment.
The `serve` command will boot up local static web server that serves the files from `.vitepress/dist` at `http://localhost:5000`. It's an easy way to check if the production build looks OK in your local environment.
You may configure the port of the server py passing `--port` flag as an argument.
@ -46,7 +50,7 @@ You may configure the port of the server py passing `--port` flag as an argument
}
```
Now the `docs:serve` method will launch the server at http://localhost:8080.
Now the `docs:serve` method will launch the server at `http://localhost:8080`.
## GitHub Pages
@ -78,10 +82,10 @@ git add -A
git commit -m 'deploy'
# if you are deploying to https://<USERNAME>.github.io
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git master
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main
# if you are deploying to https://<USERNAME>.github.io/<REPO>
# git push -f git@github.com:<USERNAME>/<REPO>.git master:gh-pages
# git push -f git@github.com:<USERNAME>/<REPO>.git main:gh-pages
cd -
```
@ -102,7 +106,7 @@ You can also run the above script in your CI setup to enable automatic deploymen
3. Run `yarn` or `npm install` locally and commit the generated lockfile (that is `yarn.lock` or `package-lock.json`).
4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages/).
4. Use the GitHub Pages deploy provider template, and follow the [Travis CI documentation](https://docs.travis-ci.com/user/deployment/pages).
```yaml
language: node_js
@ -121,7 +125,7 @@ deploy:
github_token: $GITHUB_TOKEN
keep_history: true
on:
branch: master
branch: main
```
## GitLab Pages and GitLab CI
@ -132,7 +136,7 @@ deploy:
If you are deploying to `https://<USERNAME or GROUP>.gitlab.io/<REPO>/`, for example your repository is at `https://gitlab.com/<USERNAME>/<REPO>`, then set `base` to `'/<REPO>/'`.
2. Set `dest` in `.vitepress/config.js` to `public`.
2. Set `outDir` in `.vitepress/config.js` to `../public`.
3. Create a file called `.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:
@ -149,12 +153,12 @@ pages:
paths:
- public
only:
- master
- main
```
## Netlify
1. On [Netlify](https://netlify.com), setup up a new project from GitHub with the following settings:
1. On [Netlify](https://www.netlify.com/), setup up a new project from GitHub with the following settings:
- **Build Command:** `vitepress build docs` or `yarn docs:build` or `npm run docs:build`
- **Publish directory:** `docs/.vitepress/dist`
@ -198,7 +202,7 @@ pages:
3. Deploy to surge by typing `surge docs/.vitepress/dist`.
You can also deploy to a [custom domain](http://surge.sh/help/adding-a-custom-domain) by adding `surge docs/.vitepress/dist yourdomain.com`.
You can also deploy to a [custom domain](https://surge.sh/help/adding-a-custom-domain) by adding `surge docs/.vitepress/dist yourdomain.com`.
## Heroku
@ -243,7 +247,7 @@ $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-static.git
```bash
# publish site
$ git push heroku master
$ git push heroku main
# opens a browser to view the Dashboard version of Heroku CI
$ heroku open
@ -251,9 +255,9 @@ $ heroku open
## Vercel
To deploy your VitePress app with a [Vercel for Git](https://vercel.com/docs/git), make sure it has been pushed to a Git repository.
To deploy your VitePress app with a [Vercel for Git](https://vercel.com/docs/concepts/git), make sure it has been pushed to a Git repository.
Go to https://vercel.com/import/git and import the project into Vercel using your Git of choice (GitHub, GitLab or BitBucket). Follow the wizard to select the project root with the project's `package.json` and override the build step using `yarn docs:build` or `npm run docs:build` and the output dir to be `./docs/.vitepress/dist`
Go to https://vercel.com/new and import the project into Vercel using your Git of choice (GitHub, GitLab or BitBucket). Follow the wizard to select the project root with the project's `package.json` and override the build step using `yarn docs:build` or `npm run docs:build` and the output dir to be `./docs/.vitepress/dist`
![Override Vercel Configuration](../images/vercel-configuration.png)

@ -1,3 +1,7 @@
---
sidebarDepth: 2
---
# Differences from VuePress
VitePress and VuePress have different [design goals](../index.md). Both projects share similar config naming conventions. VitePress aims to have the bare minimum features needed for authoring docs. Other features are pushed to Themes. On the other hand, VuePress has more features out-of-the-box or enabled by its ecosystem of plugins.
@ -19,11 +23,11 @@ In case you decide to move your project to VitePress, this is a list of differen
- [Plugins](https://vuepress.vuejs.org/plugin/) support, features are implemented in themes
- [permalink support](https://vuepress.vuejs.org/guide/permalinks.html)
- `.vitepress/templates`
- Components in `.vitepress/components` [are not auto registered as global components](https://vuepress.vuejs.org/)
- Components in `.vitepress/components` [are not auto registered as global components](https://vuepress.vuejs.org)
- Differences
- [Public files](https://vuepress.vuejs.org/guide/assets.html#public-files) that are directly copied to dist root moved from `.vitepress/public/` is `public/`
- [styling](https://vuepress.vuejs.org/config/#styling) `.vitepress/styles/index.styl` and `.vitepress/styles/palette.styl` is `.vitepress/style.styl`
- [App Level Enhancements](https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements) API, app enhancements `.vitepress/enhanceApp.js` is `.vitepress/theme/index.js`.
- [styling](https://vuepress.vuejs.org/config/#styling) `.vitepress/styles/index.styl` and `.vitepress/styles/palette.styl` is not supported. See [Customizing CSS](./theming#customizing-css).
- [App Level Enhancements](https://vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements) API, app enhancements `.vitepress/enhanceApp.js` is now done in `.vitepress/theme/index.js`. See [Extending the Default Theme](./theming#extending-the-default-theme).
## Markdown
@ -31,7 +35,6 @@ In case you decide to move your project to VitePress, this is a list of differen
- Support for [toml in frontmatter](https://vuepress.vuejs.org/guide/frontmatter.html#alternative-frontmatter-formats)
- [details block](https://vuepress.vuejs.org/guide/markdown.html#custom-containers)
- [markdown slots](https://vuepress.vuejs.org/guide/markdown-slot.html)
guide/using-vue.html#using-components).
- `~` prefix to explicitly specify a url is a [webpack module request](https://vuepress.vuejs.org/guide/assets.html#relative-urls)
## Site Config
@ -67,7 +70,7 @@ In case you decide to move your project to VitePress, this is a list of differen
- `algolia` is `search.algolia`
- `searchPlaceholder` is `search.placeholder`
# Default Theme
## Default Theme
- Missing
- [`<code-group>` and `<code-block>`](https://vuepress.vuejs.org/theme/default-theme-config.html#code-groups-and-code-blocks)

@ -9,7 +9,7 @@ editLink: true
---
```
Between the triple-dashed lines, you can set [predefined variables](#predefined-variables), or even create custom ones of your own. These variables can be used via the <code>$frontmatter</code> variable.
Between the triple-dashed lines, you can set [predefined variables](#predefined-variables), or even create custom ones of your own. These variables can be used via the special <code>$frontmatter</code> variable.
Heres an example of how you could use it in your Markdown file:
@ -63,7 +63,6 @@ head:
- name: keywords
content: super duper SEO
---
```
### navbar

@ -44,7 +44,7 @@ This section will help you build a basic VitePress documentation site from groun
$ yarn docs:dev
```
VitePress will start a hot-reloading development server at http://localhost:3000.
VitePress will start a hot-reloading development server at `http://localhost:3000`.
By now, you should have a basic but functional VitePress documentation site.

@ -4,7 +4,7 @@ VitePress comes with few built-in component that can be used globally. You may u
## Content
The `Content` component displays the rendered markdown contents. Useful [when creating your own theme](http://localhost:3000/guide/customization.html).
The `Content` component displays the rendered markdown contents. Useful [when creating your own theme](./theming).
```vue
<template>
@ -30,4 +30,3 @@ If you are using or demoing components that are not SSR-friendly (for example, c
## OutboundLink
The indicator `OutboundLink` is used to denote external links. In VitePress, this component has been followed by every external link.

@ -1,89 +0,0 @@
# Global Computed
In VitePress, some core [computed properties](https://v3.vuejs.org/guide/computed.html#computed-properties) can be used by the default theme or custom themes. Or directly in Markdown pages using vue, for example using `$frontmatter.title` to access the title defined in the frontmatter section of the page.
## $site
This is the `$site` value of the site you're currently reading:
```js
{
base: '/',
lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',
head: [],
locales: {},
themeConfig: $themeConfig
}
```
## $themeConfig
Refers to `$site.themeConfig`.
```js
{
locales: {},
repo: 'vuejs/vitepress',
docsDir: 'docs',
editLinks: true,
editLinkText: 'Edit this page on GitHub',
lastUpdated: 'Last Updated',
nav: [...],
sidebar: { ... }
}
```
## $page
This is the `$page` value of the page you're currently reading:
```js
{
relativePath: 'guide/global-computed.md',
title: 'Global Computed',
headers: [
{ level: 2, title: '$site', slug: 'site' },
{ level: 2, title: '$page', slug: '$page' },
...
],
frontmatter: $frontmatter,
lastUpdated: 1606297645000
}
```
## $frontmatter
Reference of `$page.frontmatter`.
```js
{
title: 'Docs with VitePress',
editLink: true
}
```
## $lang
The language of the current page. Default: `en-US`.
## $localePath
The locale path prefix for the current page. Default: `/`.
## $title
Value of the `<title>` label used for the current page.
## $description
The content value of the `<meta name= "description" content= "...">` for the current page.
## $withBase
Helper method to generate correct path by prepending the `base` path configured in `.vitepress/config.js`. It's useful when you want to link to [public files with base path](./assets#public-files).
```html
<img :src="$withBase('/foo.png')" alt="foo" />
```

@ -1,3 +1,7 @@
---
sidebarDepth: 3
---
# Markdown Extensions
## Header Anchors
@ -28,9 +32,9 @@ For example, given the following directory structure:
And providing you are in `foo/one.md`:
```md
[Home](/) <!-- sends the user to the root README.md -->
[Home](/) <!-- sends the user to the root index.md -->
[foo](/foo/) <!-- sends the user to index.html of directory foo -->
[foo heading](./#heading) <!-- anchors user to a heading in the foo README file -->
[foo heading](./#heading) <!-- anchors user to a heading in the foo index file -->
[bar - three](../bar/three) <!-- you can omit extention -->
[bar - three](../bar/three.md) <!-- you can append .md -->
[bar - four](../bar/four.html) <!-- or you can append .html -->
@ -49,19 +53,18 @@ Outbound links automatically get `target="_blank" rel="noopener noreferrer"`:
## Frontmatter
[YAML frontmatter](https://jekyllrb.com/docs/frontmatter/) is supported out of the box:
[YAML frontmatter](https://jekyllrb.com/docs/front-matter/) is supported out of the box:
```yaml
---
title: Blogging Like a Hacker
lang: en-US
---
```
This data will be available to the rest of the page, along with all custom and theming components.
For more details, see [Frontmatter](./frontmatter.md).
For more details, see [Frontmatter](./frontmatter).
## GitHub-Style Tables
@ -124,6 +127,10 @@ Custom containers can be defined by their types, titles, and contents.
This is a tip
:::
::: info
This is an info box
:::
::: warning
This is a warning
:::
@ -131,6 +138,10 @@ This is a warning
::: danger
This is a dangerous warning
:::
::: details
This is a details block, which does not work in Internet Explorer or Edge.
:::
```
**Output**
@ -139,6 +150,10 @@ This is a dangerous warning
This is a tip
:::
::: info
This is an info box
:::
::: warning
This is a warning
:::
@ -147,25 +162,45 @@ This is a warning
This is a dangerous warning
:::
::: details
This is a details block, which does not work in Internet Explorer or Edge.
:::
### Custom Title
**Input**
```md
````md
::: danger STOP
Danger zone, do not proceed
:::
::: details Click me to view the code
```js
console.log('Hello, VitePress!')
```
:::
````
**Output**
::: danger STOP
Danger zone, do not proceed
:::
::: details Click me to view the code
```js
console.log('Hello, VitePress!')
```
:::
## Syntax Highlighting in Code Blocks
VitePress uses [Prism](https://prismjs.com/) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block:
VitePress uses [Prism](https://prismjs.com) to highlight language syntax in Markdown code blocks, using coloured text. Prism supports a wide variety of programming languages. All you need to do is append a valid language alias to the beginning backticks for the code block:
**Input**
@ -323,17 +358,86 @@ module.exports = {
}
</style>
## Import Code Snippets
You can import code snippets from existing files via following syntax:
```md
<<< @/filepath
```
It also supports [line highlighting](#line-highlighting-in-code-blocks):
```md
<<< @/filepath{highlightLines}
```
**Input**
```md
<<< @/snippets/snippet.js{2}
```
**Code file**
<!--lint disable strong-marker-->
<<< @/snippets/snippet.js
<!--lint enable strong-marker-->
**Output**
<!--lint disable strong-marker-->
<<< @/snippets/snippet.js{2}
<!--lint enable strong-marker-->
::: tip
The value of `@` corresponds to the source root. By default it's the VitePress project root, unless `srcDir` is configured.
:::
You can also use a [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding) to only include the corresponding part of the code file. You can provide a custom region name after a `#` following the filepath (`snippet` by default):
**Input**
```md
<<< @/snippets/snippet-with-region.js{1}
```
**Code file**
<!--lint disable strong-marker-->
<<< @/snippets/snippet-with-region.js
<!--lint enable strong-marker-->
**Output**
<!--lint disable strong-marker-->
<<< @/snippets/snippet-with-region.js#snippet{1}
<!--lint enable strong-marker-->
## Advanced Configuration
VitePress uses [markdown-it](https://github.com/markdown-it/markdown-it) as the Markdown renderer. A lot of the extensions above are implemented via custom plugins. You can further customize the `markdown-it` instance using the `markdown` option in `.vitepress/config.js`:
```js
const anchor = require('markdown-it-anchor')
module.exports = {
markdown: {
// options for markdown-it-anchor
anchor: { permalink: false },
// https://github.com/valeriangalliat/markdown-it-anchor#permalinks
anchor: {
permalink: anchor.permalink.headerLink()
},
// options for markdown-it-toc
// options for markdown-it-table-of-contents
toc: { includeLevel: [1, 2] },
config: (md) => {

@ -0,0 +1,155 @@
# Theming
## Using a Custom Theme
You can enable a custom theme by adding the `.vitepress/theme/index.js` file (the "theme entry file").
```bash
.
├─ docs
│ ├─ .vitepress
│ │ ├─ theme
│ │ │ └─ index.js
│ │ └─ config.js
│ └─ index.md
└─ package.json
```
A VitePress custom theme is simply an object containing three properties and is defined as follows:
```ts
interface Theme {
Layout: Component // Vue 3 component
NotFound?: Component
enhanceApp?: (ctx: EnhanceAppContext) => void
}
interface EnhanceAppContext {
app: App // Vue 3 app instance
router: Router // VitePress router instance
siteData: Ref<SiteData>
}
```
The theme entry file should export the theme as its default export:
```js
// .vitepress/theme/index.js
import Layout from './Layout.vue'
export default {
Layout,
NotFound: () => 'custom 404', // <- this is a Vue 3 functional component
enhanceApp({ app, router, siteData }) {
// app is the Vue 3 app instance from `createApp()`. router is VitePress'
// custom router. `siteData` is a `ref` of current site-level metadata.
}
}
```
...where the `Layout` component could look like this:
```vue
<!-- .vitepress/theme/Layout.vue -->
<template>
<h1>Custom Layout!</h1>
<Content /><!-- this is where markdown content will be rendered -->
</template>
```
The default export is the only contract for a custom theme. Inside your custom theme, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./using-vue#browser-api-access-restrictions).
To distribute a theme, simply export the object in your package entry. To consume an external theme, import and re-export it from the custom theme entry:
```js
// .vitepress/theme/index.js
import Theme from 'awesome-vitepress-theme'
export default Theme
```
## Extending the Default Theme
If you want to extend and customize the default theme, you can import it from `vitepress/theme` and augment it in a custom theme entry. Here are some examples of common customizations:
### Registering Global Components
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp({ app }) {
// register global components
app.component('MyGlobalComponent' /* ... */)
}
}
```
Since we are using Vite, you can also leverage Vite's [glob import feature](https://vitejs.dev/guide/features.html#glob-import) to auto register a directory of components.
### Customizing CSS
The default theme CSS is customizable by overriding root level CSS variables:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
```
```css
/* .vitepress/theme/custom.css */
:root {
--c-brand: #646cff;
--c-brand-light: #747bff;
}
```
See [default theme CSS variables](https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css) that can be overridden.
### Layout Slots
The default theme's `<Layout/>` component has a few slots that can be used to inject content at certain locations of the page. Here's an example of injecting a component into the top of the sidebar:
```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
...DefaultTheme,
// override the Layout with a wrapper component that injects the slots
Layout: MyLayout
}
```
```vue
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout>
<template #sidebar-top>My custom sidebar top content</template>
</Layout>
</template>
```
Full list of slots available in the default theme layout:
- `navbar-search`
- `sidebar-top`
- `sidebar-bottom`
- `page-top-ads`
- `page-top`
- `page-bottom`
- `page-bottom-ads`
- Only when `home: true` is enabled via frontmatter:
- `home-hero`
- `home-features`
- `home-footer`

@ -1,59 +1,12 @@
# Using Vue in Markdown
## Browser API Access Restrictions
Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the [universal code requirements](https://ssr.vuejs.org/en/universal.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks.
If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the built-in `<ClientOnly>` component:
``` md
<ClientOnly>
<NonSSRFriendlyComponent/>
</ClientOnly>
```
Note this does not fix components or libraries that access Browser APIs **on import**. To use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks:
``` vue
<script>
export default {
mounted () {
import('./lib-that-access-window-on-import').then(module => {
// use code
})
}
}
</script>
```
If your module `export default` a Vue component, you can register it dynamically:
```vue
<template>
<component v-if="dynamicComponent" :is="dynamicComponent"></component>
</template>
<script>
export default {
data() {
return {
dynamicComponent: null
}
},
mounted () {
import('./lib-that-access-window-on-import').then(module => {
this.dynamicComponent = module.default
})
}
}
</script>
```
---
sidebarDepth: 3
---
**Also see:**
# Using Vue in Markdown
- [Vue.js > Dynamic Components](https://v3.vuejs.org/guide/component-dynamic-async.html)
In VitePress, each markdown file is compiled into HTML and then processed as a Vue Single-File Component. 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.
It is also important to know that VitePress leverages Vue 3's compiler to automatically detect and optimize the purely static parts of the markdown. Static contents are optimized into single placeholder nodes and eliminated from the page's JavaScript payload. They are also skipped during client-side hydration. In short, you only pay for the dynamic parts on any given page.
## Templating
@ -63,7 +16,7 @@ Each Markdown file is first compiled into HTML and then passed on as a Vue compo
**Input**
``` md
```md
{{ 1 + 1 }}
```
@ -77,7 +30,7 @@ Directives also work:
**Input**
``` md
```md
<span v-for="i in 3">{{ i }} </span>
```
@ -87,17 +40,22 @@ Directives also work:
### Access to Site & Page Data
The compiled component has access to the [site metadata and computed properties](./global-computed.md). For example:
You can use the [`useData` helper](./api#usedata) in a `<script>` block and expose the data to the page.
**Input**
``` md
{{ $page }}
```md
<script setup>
import { useData } from 'vitepress'
const { page } = useData()
</script>
<pre>{{ page }}</pre>
```
**Output**
``` json
```json
{
"path": "/using-vue.html",
"title": "Using Vue in Markdown",
@ -111,7 +69,7 @@ By default, fenced code blocks are automatically wrapped with `v-pre`. To displa
**Input**
``` md
```md
::: v-pre
`{{ This will be displayed as-is }}`
:::
@ -129,9 +87,13 @@ When you need to have more flexibility, VitePress allows you to extend your auth
### Importing components in markdown
If your components are going to be used in only a few places, the recommended way to use them is to importing the components in the file where it is used.
If your components are going to be used in only a few places, the recommended way to use them is to importing the components in the file where it is used.
```md
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>
# Docs
This is a .md using a custom component
@ -141,17 +103,13 @@ This is a .md using a custom component
## More docs
...
<script setup>
import CustomComponent from '../components/CustomComponent.vue'
</script>
```
### Registering global components in the theme
If the components are going to be used across several pages in the docs, they can be registered globally in the theme (or as part of extending the default VitePress theme). Check out the [Customization Guide](./customization.md) for more information.
If the components are going to be used across several pages in the docs, they can be registered globally in the theme (or as part of extending the default VitePress theme). Check out the [Theming Guide](./theming) for more information.
In `.vitepress/theme/index.js`, the `enhanceApp` function receives the Vue `app` instance so you can [register components](https://v3.vuejs.org/guide/component-registration.html#component-registration) as you would do in a regular Vue application.
In `.vitepress/theme/index.js`, the `enhanceApp` function receives the Vue `app` instance so you can [register components](https://vuejs.org/guide/components/registration.html) as you would do in a regular Vue application.
```js
import DefaultTheme from 'vitepress/theme'
@ -180,9 +138,9 @@ Make sure a custom components name either contains a hyphen or is in PascalCa
You can use Vue components in the headers, but note the difference between the following syntaxes:
| Markdown | Output HTML | Parsed Header |
|--------|-------------|----------------|
| <pre v-pre><code> # text &lt;Tag/&gt; </code></pre> | `<h1>text <Tag/></h1>` | `text` |
| Markdown | Output HTML | Parsed Header |
| ------------------------------------------------------- | ----------------------------------------- | ------------- |
| <pre v-pre><code> # text &lt;Tag/&gt; </code></pre> | `<h1>text <Tag/></h1>` | `text` |
| <pre v-pre><code> # text \`&lt;Tag/&gt;\` </code></pre> | `<h1>text <code>&lt;Tag/&gt;</code></h1>` | `text <Tag/>` |
The HTML wrapped by `<code>` will be displayed as-is; only the HTML that is **not** wrapped will be parsed by Vue.
@ -208,7 +166,7 @@ npm install -D stylus
Then you can use the following in Markdown and theme components:
``` vue
```vue
<style lang="sass">
.title
font-size: 20px
@ -242,8 +200,62 @@ export default {
## Built-In Components
VitePress provides Built-In Vue Components like `ClientOnly` and `OutboundLink`, check out the [Global Component Guide](./global-component.md) for more information.
VitePress provides Built-In Vue Components like `ClientOnly` and `OutboundLink`, check out the [Global Component Guide](./global-component) for more information.
**Also see:**
- [Using Components In Headers](#using-components-in-headers)
## Browser API Access Restrictions
Because VitePress applications are server-rendered in Node.js when generating static builds, any Vue usage must conform to the [universal code requirements](https://vuejs.org/guide/scaling-up/ssr.html). In short, make sure to only access Browser / DOM APIs in `beforeMount` or `mounted` hooks.
If you are using or demoing components that are not SSR-friendly (for example, contain custom directives), you can wrap them inside the built-in `<ClientOnly>` component:
```md
<ClientOnly>
<NonSSRFriendlyComponent/>
</ClientOnly>
```
Note this does not fix components or libraries that access Browser APIs **on import**. To use code that assumes a browser environment on import, you need to dynamically import them in proper lifecycle hooks:
```vue
<script>
export default {
mounted() {
import('./lib-that-access-window-on-import').then((module) => {
// use code
})
}
}
</script>
```
If your module `export default` a Vue component, you can register it dynamically:
```vue
<template>
<component v-if="dynamicComponent" :is="dynamicComponent"></component>
</template>
<script>
export default {
data() {
return {
dynamicComponent: null
}
},
mounted() {
import('./lib-that-access-window-on-import').then((module) => {
this.dynamicComponent = module.default
})
}
}
</script>
```
**Also see:**
- [Using Components In Headers](#using-components-in-headers)
- [Vue.js > Dynamic Components](https://vuejs.org/guide/essentials/component-basics.html#dynamic-components)

@ -1,26 +1,28 @@
---
sidebarDepth: 2
---
# What is VitePress?
::: warning WARNING
VitePress is early WIP! Currently the focus is on making Vite stable and feature complete first. It is not recommended to use this for anything serious yet.
VitePress is currently in 0.x status. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases.
:::
VitePress is [VuePress](https://vuepress.vuejs.org/)' little brother, built on top of [Vite](https://github.com/vitejs/vite).
VitePress is [VuePress](https://vuepress.vuejs.org)' little brother, built on top of [Vite](https://github.com/vitejs/vite).
## Motivation
We love VuePress, but being built on top of Webpack, the time it takes to spin up the dev server for a simple doc site with a few pages is just becoming unbearable. Even HMR updates can take up to seconds to reflect in the browser!
As a reference, the [Composition API RFC repo](https://github.com/vuejs/composition-api-rfc) is just two pages, but it takes 4 seconds to spin up the server and almost 2 seconds for any edit to reflect in the browser.
We love VuePress v1, but being built on top of Webpack, the time it takes to spin up the dev server for a simple doc site with a few pages is just becoming unbearable. Even HMR updates can take up to seconds to reflect in the browser!
Fundamentally, this is because VuePress is a Webpack app under the hood. Even with just two pages, it's a full on Webpack project (including all the theme source files) being compiled. It gets even worse when the project has many pages every page must first be fully compiled before the server can even display anything!
Fundamentally, this is because VuePress v1 is a Webpack app under the hood. Even with just two pages, it's a full on Webpack project (including all the theme source files) being compiled. It gets even worse when the project has many pages every page must first be fully compiled before the server can even display anything!
Incidentally, Vite solves these problems really well: nearly instant server start, an on-demand compilation that only compiles the page being served, and lightning-fast HMR. Plus, there are a few additional design issues I have noted in VuePress over time but never had the time to fix due to the amount of refactoring it would require.
Incidentally, Vite solves these problems really well: nearly instant server start, an on-demand compilation that only compiles the page being served, and lightning-fast HMR. Plus, there are a few additional design issues I have noted in VuePress v1 over time but never had the time to fix due to the amount of refactoring it would require.
Now, with Vite and Vue 3, it is time to rethink what a "Vue-powered static site generator" can really be.
## Improvements Over VuePress
## Improvements Over VuePress v1
There're couple of things that are improved from VuePress....
There're couple of things that are improved from VuePress v1....
### It Uses Vue 3
@ -49,4 +51,6 @@ VitePress is future oriented: VitePress only targets browsers that support nativ
## Will This Become The Next VuePress in The Future?
Probably not. It's currently under a different name so that we don't over commit to the compatibility with the current VuePress ecosystem (mostly themes and plugins). We'll see how close we can get without compromising the design goals listed above. But the overall idea is that VitePress will have a drastically more minimal theming API (preferring JavaScript APIs instead of file layout conventions) and likely no plugins (all customization is done in themes).
We already have [vuepress-next](https://github.com/vuepress/vuepress-next), which would be the next major version of VuePress. It also makes lots of improvements over VuePress v1, and also supports Vite now.
VitePress is not compatible with the current VuePress ecosystem (mostly themes and plugins). The overall idea is that VitePress will have a drastically more minimal theming API (preferring JavaScript APIs instead of file layout conventions) and likely no plugins (all customization is done in themes).

@ -0,0 +1,7 @@
// #region snippet
function foo() {
// ..
}
// #endregion snippet
export default foo

@ -0,0 +1,3 @@
export default function () {
// ..
}

@ -2,8 +2,11 @@
"private": true,
"name": "vitepress-example-minimal",
"scripts": {
"dev": "node ../../bin/vitepress dev",
"build": "node ../../bin/vitepress build",
"serve": "node ../../bin/vitepress serve"
"dev": "vitepress dev",
"build": "vitepress build",
"serve": "vitepress serve"
},
"devDependencies": {
"vitepress": "workspace:*"
}
}

@ -1,13 +0,0 @@
module.exports = {
preset: 'ts-jest',
rootDir: __dirname,
moduleNameMapper: {
'^src/(.*)$': '<rootDir>/src/$1',
'^client/(.*)$': '<rootDir>/src/client/$1',
'^node/(.*)$': '<rootDir>/src/node/$1',
'^shared/(.*)$': '<rootDir>/src/shared/$1',
'^tests/(.*)$': '<rootDir>/__tests__/$1'
},
testMatch: ['<rootDir>/__tests__/**/*.spec.ts'],
testPathIgnorePatterns: ['/node_modules/']
}

@ -0,0 +1,7 @@
[build.environment]
NODE_VERSION = "16"
NPM_FLAGS = "--version" # prevent Netlify npm install
[build]
publish = "docs/.vitepress/dist"
command = "npx pnpm i --store=node_modules/.pnpm-store && npm run ci-docs"

@ -1,6 +1,6 @@
{
"name": "vitepress",
"version": "0.12.2",
"version": "0.22.3",
"description": "Vite & Vue powered static site generator",
"main": "dist/node/index.js",
"typings": "types/index.d.ts",
@ -9,32 +9,41 @@
},
"files": [
"bin",
"lib",
"dist",
"types"
"types",
"client.d.ts",
"theme.d.ts"
],
"scripts": {
"dev": "yarn dev-shared && yarn dev-start",
"dev": "run-s dev-shared dev-start",
"dev-start": "run-p dev-client dev-node dev-watch",
"dev-client": "tsc -w -p src/client",
"dev-node": "tsc -w -p src/node",
"dev-shared": "node scripts/copyShared",
"dev-watch": "node scripts/watchAndCopy",
"build": "rimraf -rf dist && node scripts/copyShared && tsc -p src/client && tsc -p src/node && node scripts/copyClient",
"lint": "yarn lint:js && yarn lint:ts",
"build": "run-s build-prepare build-client build-node build-types",
"build-prepare": "rimraf -rf dist && node scripts/copyShared",
"build-client": "tsc -p src/client && node scripts/copyClient",
"build-node": "rollup -c scripts/rollup.config.js",
"build-types": "run-s build-types-client build-types-node",
"build-types-client": "tsc -p src/client --declaration --emitDeclarationOnly --outDir dist/temp && api-extractor run -c api-extractor.client.json && rimraf dist/temp",
"build-types-node": "tsc -p src/node --declaration --emitDeclarationOnly --outDir dist/temp && api-extractor run -c api-extractor.node.json && rimraf dist/temp",
"lint": "run-s lint:js lint:ts",
"lint:js": "prettier --check --write \"{bin,docs,scripts,src}/**/*.js\"",
"lint:ts": "prettier --check --write --parser typescript \"{__tests__,src,docs,types}/**/*.ts\"",
"test": "jest",
"test": "vitest run __tests__ -c __tests__/vitest.config.js --global",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
"release": "node scripts/release.js",
"docs": "run-p dev docs-dev",
"docs-dev": "node ./bin/vitepress dev docs",
"docs-debug": "node --inspect-brk ./bin/vitepress dev docs",
"docs-build": "yarn build && node ./bin/vitepress build docs",
"docs-serve": "node ./bin/vitepress serve docs"
"docs-build": "npm run build && node ./bin/vitepress build docs",
"docs-build-only": "node ./bin/vitepress build docs",
"docs-serve": "node ./bin/vitepress serve docs",
"ci-docs": "run-s build docs-build"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
},
"gitHooks": {
"pre-commit": "lint-staged"
@ -54,7 +63,7 @@
"keywords": [
"vite",
"vue",
"vuepress"
"vitepress"
],
"author": "Evan You",
"license": "MIT",
@ -63,56 +72,76 @@
"url": "https://github.com/vuejs/vitepress/issues"
},
"dependencies": {
"@docsearch/css": "^1.0.0-alpha.28",
"@docsearch/js": "^1.0.0-alpha.28",
"@vitejs/plugin-vue": "^1.1.4",
"@vue/compiler-sfc": "^3.0.5",
"@vue/server-renderer": "^3.0.5",
"chalk": "^4.1.0",
"compression": "^1.7.4",
"debug": "^4.1.1",
"diacritics": "^1.3.0",
"escape-html": "^1.0.3",
"fs-extra": "^9.1.0",
"globby": "^11.0.2",
"gray-matter": "^4.0.2",
"lru-cache": "^6.0.0",
"markdown-it": "^10.0.0",
"markdown-it-anchor": "^5.2.7",
"markdown-it-container": "^2.0.0",
"markdown-it-emoji": "^1.4.0",
"markdown-it-table-of-contents": "^0.4.4",
"minimist": "^1.2.5",
"ora": "^5.3.0",
"polka": "^0.5.2",
"prismjs": "^1.23.0",
"sirv": "^1.0.11",
"vite": "^2.0.5",
"vue": "^3.0.5"
"@docsearch/css": "^3.0.0-alpha.41",
"@docsearch/js": "^3.0.0-alpha.41",
"@vitejs/plugin-vue": "^2.2.0",
"prismjs": "^1.25.0",
"vite": "^2.8.1",
"vue": "^3.2.31"
},
"devDependencies": {
"@microsoft/api-extractor": "^7.18.9",
"@rollup/plugin-alias": "^3.1.5",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"@types/compression": "^1.7.0",
"@types/fs-extra": "^9.0.1",
"@types/jest": "^26.0.20",
"@types/koa": "^2.11.7",
"@types/cross-spawn": "^6.0.2",
"@types/debug": "^4.1.7",
"@types/fs-extra": "^9.0.11",
"@types/koa": "^2.13.1",
"@types/koa-static": "^4.0.1",
"@types/lru-cache": "^5.1.0",
"@types/markdown-it": "^10.0.2",
"@types/node": "^14.14.25",
"@types/postcss-load-config": "^2.0.1",
"@types/markdown-it": "^12.0.1",
"@types/micromatch": "^4.0.2",
"@types/node": "^15.6.1",
"@types/polka": "^0.5.3",
"chalk": "^4.1.1",
"chokidar": "^3.5.1",
"conventional-changelog-cli": "^2.1.0",
"compression": "^1.7.4",
"conventional-changelog-cli": "^2.1.1",
"cross-spawn": "^7.0.3",
"debug": "^4.3.2",
"diacritics": "^1.3.0",
"enquirer": "^2.3.6",
"esbuild": "^0.14.0",
"escape-html": "^1.0.3",
"execa": "^5.0.0",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0",
"globby": "^11.0.3",
"gray-matter": "^4.0.3",
"lint-staged": "^11.0.0",
"lru-cache": "^6.0.0",
"markdown-it": "^12.3.2",
"markdown-it-anchor": "^8.4.1",
"markdown-it-attrs": "^4.1.3",
"markdown-it-container": "^3.0.0",
"markdown-it-emoji": "^2.0.0",
"markdown-it-table-of-contents": "^0.6.0",
"micromatch": "^4.0.4",
"minimist": "^1.2.5",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5",
"ora": "^5.4.0",
"polka": "^0.5.2",
"prettier": "^2.3.0",
"rimraf": "^3.0.2",
"rollup": "^2.38.5",
"semver": "^7.3.2",
"ts-jest": "^26.5.1",
"typescript": "^4.1.4",
"rollup": "^2.56.3",
"rollup-plugin-esbuild": "^4.8.2",
"semver": "^7.3.5",
"sirv": "^1.0.12",
"typescript": "^4.3.2",
"vitest": "^0.1.19",
"yorkie": "^2.0.0"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"react",
"react-dom",
"@types/react"
]
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,3 @@
packages:
- examples/*
- docs

@ -2,6 +2,6 @@ const fs = require('fs-extra')
const glob = require('globby')
glob.sync('src/shared/**/*.ts').forEach((file) => {
fs.copy(file, file.replace(/^src\//, 'src/node/'))
fs.copy(file, file.replace(/^src\//, 'src/client/'))
fs.copy(file, file.replace(/^src\/shared\//, 'src/node/'))
fs.copy(file, file.replace(/^src\/shared\//, 'src/client/'))
})

@ -9,7 +9,6 @@ const currentVersion = require('../package.json').version
const versionIncrements = ['patch', 'minor', 'major']
const inc = (i) => semver.inc(currentVersion, i)
const bin = (name) => path.resolve(__dirname, `../node_modules/.bin/${name}`)
const run = (bin, args, opts = {}) =>
execa(bin, args, { stdio: 'inherit', ...opts })
const step = (msg) => console.log(chalk.cyan(msg))
@ -57,12 +56,12 @@ async function main() {
// Build the package.
step('\nBuilding the package...')
await run('yarn', ['build'])
await run('pnpm', ['build'])
// Generate the changelog.
step('\nGenerating the changelog...')
await run('yarn', ['changelog'])
await run('yarn', ['prettier', '--write', 'CHANGELOG.md'])
await run('pnpm', ['changelog'])
await run('pnpm', ['prettier', '--write', 'CHANGELOG.md'])
const { yes: changelogOk } = await prompt({
type: 'confirm',
@ -82,13 +81,7 @@ async function main() {
// Publish the package.
step('\nPublishing the package...')
await run('yarn', [
'publish',
'--new-version',
targetVersion,
'--no-commit-hooks',
'--no-git-tag-version'
])
await run('pnpm', ['publish', '--ignore-scripts', '--no-git-checks'])
// Push to GitHub.
step('\nPushing to GitHub...')

@ -0,0 +1,35 @@
import { defineConfig } from 'rollup'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import esbuild from 'rollup-plugin-esbuild'
import json from '@rollup/plugin-json'
import alias from '@rollup/plugin-alias'
import { resolve } from 'path'
const r = (p) => resolve(__dirname, '../', p)
const pkg = require('../package.json')
export default defineConfig({
input: [r('src/node/index.ts'), r('src/node/cli.ts')],
output: {
format: 'cjs',
dir: r('dist/node')
},
external: [...Object.keys(pkg.dependencies), 'buffer', 'punycode'],
plugins: [
alias({
entries: {
'readable-stream': 'stream'
}
}),
commonjs(),
nodeResolve(),
esbuild({
target: 'node12'
}),
json()
],
onwarn(warning, warn) {
if (warning.code !== 'EVAL') warn(warning)
}
})

@ -1,18 +1,20 @@
const fs = require('fs-extra')
const chokidar = require('chokidar')
const { normalizePath } = require('vite')
function toClientAndNode(method, file) {
file = normalizePath(file)
if (method === 'copy') {
fs.copy(file, file.replace(/^src\//, 'src/node/'))
fs.copy(file, file.replace(/^src\//, 'src/client/'))
fs.copy(file, file.replace(/^src\/shared\//, 'src/node/'))
fs.copy(file, file.replace(/^src\/shared\//, 'src/client/'))
} else if (method === 'remove') {
fs.remove(file.replace(/^src\//, 'src/node/'))
fs.remove(file.replace(/^src\//, 'src/client/'))
fs.remove(file.replace(/^src\/shared\//, 'src/node/'))
fs.remove(file.replace(/^src\/shared\//, 'src/client/'))
}
}
function toDist(file) {
return file.replace(/^src\//, 'dist/')
return normalizePath(file).replace(/^src\//, 'dist/')
}
// copy shared files to the client and node directory whenever they change.

@ -5,6 +5,9 @@ export const Content = defineComponent({
name: 'VitePressContent',
setup() {
const route = useRoute()
return () => (route.component ? h(route.component) : null)
return () =>
h('div', { style: { position: 'relative' } }, [
route.component ? h(route.component) : null
])
}
})

@ -1,26 +1,28 @@
<template>
<div class="debug" :class="{ open }" ref="el" @click="open = !open">
<p class="title">Debug</p>
<pre class="block">$page {{ $page }}</pre>
<pre class="block">$siteByRoute {{ $siteByRoute }}</pre>
<pre class="block">$site {{ $site }}</pre>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ref, watch, reactive } from 'vue'
import { useData } from '../data'
const data = useData()
const el = ref<HTMLElement | null>(null)
const open = ref(false)
// FIXME: remove in next Vue release
const tempData = reactive(data)
watch(open, (value) => {
if (value === false) {
if (!value) {
el.value!.scrollTop = 0
}
})
</script>
<template>
<div class="debug" :class="{ open }" ref="el" @click="open = !open">
<p class="title">Debug</p>
<pre class="block">{{ tempData }}</pre>
</div>
</template>
<style scoped>
.debug {
box-sizing: border-box;

@ -1,9 +0,0 @@
import { Ref, computed } from 'vue'
import { usePageData } from './pageData'
export type FrontmatterRef = Ref<Record<string, any>>
export function useFrontmatter() {
const pageData = usePageData()
return computed(() => pageData.value.frontmatter)
}

@ -1,11 +1,11 @@
import { watchEffect, Ref } from 'vue'
import { HeadConfig, SiteData } from '../../../../types/shared'
import { HeadConfig, SiteData } from '../../shared'
import { Route } from '../router'
export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
const metaTags: HTMLElement[] = Array.from(document.querySelectorAll('meta'))
let managedHeadTags: HTMLElement[] = []
let isFirstUpdate = true
const updateHeadTags = (newTags: HeadConfig[]) => {
if (import.meta.env.PROD && isFirstUpdate) {
// in production, the initial meta tags are already pre-rendered so we
@ -13,15 +13,44 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
isFirstUpdate = false
return
}
metaTags.forEach((el) => document.head.removeChild(el))
metaTags.length = 0
if (newTags && newTags.length) {
newTags.forEach((headConfig) => {
const el = createHeadElement(headConfig)
document.head.appendChild(el)
metaTags.push(el)
})
const newEls: HTMLElement[] = []
const commonLength = Math.min(managedHeadTags.length, newTags.length)
for (let i = 0; i < commonLength; i++) {
let el = managedHeadTags[i]
const [tag, attrs, innerHTML = ''] = newTags[i]
if (el.tagName.toLocaleLowerCase() === tag) {
for (const key in attrs) {
if (el.getAttribute(key) !== attrs[key]) {
el.setAttribute(key, attrs[key])
}
}
for (let i = 0; i < el.attributes.length; i++) {
const name = el.attributes[i].name
if (!(name in attrs)) {
el.removeAttribute(name)
}
}
if (el.innerHTML !== innerHTML) {
el.innerHTML = innerHTML
}
} else {
document.head.removeChild(el)
el = createHeadElement(newTags[i])
document.head.append(el)
}
newEls.push(el)
}
managedHeadTags
.slice(commonLength)
.forEach((el) => document.head.removeChild(el))
newTags.slice(commonLength).forEach((headConfig) => {
const el = createHeadElement(headConfig)
document.head.appendChild(el)
newEls.push(el)
})
managedHeadTags = newEls
}
watchEffect(() => {
@ -30,25 +59,17 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
const pageTitle = pageData && pageData.title
const pageDescription = pageData && pageData.description
const frontmatterHead = pageData && pageData.frontmatter.head
// update title and description
document.title = (pageTitle ? pageTitle + ` | ` : ``) + siteData.title
document
.querySelector(`meta[name=description]`)!
.setAttribute('content', pageDescription || siteData.description)
updateHeadTags([
['meta', { charset: 'utf-8' }],
[
'meta',
{
name: 'viewport',
content: 'width=device-width,initial-scale=1'
}
],
[
'meta',
{
name: 'description',
content: pageDescription || siteData.description
}
],
...siteData.head,
...((frontmatterHead && filterOutHeadDescription(frontmatterHead)) || [])
// site head can only change during dev
...(import.meta.env.DEV ? siteData.head : []),
...(frontmatterHead ? filterOutHeadDescription(frontmatterHead) : [])
])
})
}

@ -1,11 +0,0 @@
import { Ref, computed } from 'vue'
import { PageData } from '/@types/shared'
import { Route, useRoute } from '../router'
export type PageDataRef = Ref<PageData>
export function usePageData(route?: Route) {
const r = route || useRoute()
return computed(() => r.data)
}

@ -1,22 +0,0 @@
import serialized from '@siteData'
import { SiteData } from '/@types/shared'
import { Ref, ref, readonly } from 'vue'
export type SiteDataRef<T = any> = Ref<SiteData<T>>
export const siteDataRef: Ref<SiteData> = ref(parse(serialized))
export function useSiteData<T = any>() {
return siteDataRef as Ref<SiteData<T>>
}
function parse(data: string): SiteData {
return readonly(JSON.parse(data)) as SiteData
}
// hmr
if (import.meta.hot) {
import.meta.hot!.accept('/@siteData', (m) => {
siteDataRef.value = parse(m.default)
})
}

@ -1,12 +0,0 @@
import { computed } from 'vue'
import { resolveSiteDataByRoute } from '/@shared/config'
import { siteDataRef } from './siteData'
import { Route, useRoute } from '../router'
export function useSiteDataByRoute(route?: Route) {
const r = route || useRoute()
return computed(() => {
return resolveSiteDataByRoute(siteDataRef.value, r.path)
})
}

@ -0,0 +1,73 @@
import { InjectionKey, Ref, shallowRef, readonly, computed, inject } from 'vue'
import { Route } from './router'
import serializedSiteData from '@siteData'
import { resolveSiteDataByRoute, PageData, SiteData } from '../shared'
import { withBase } from './utils'
export const dataSymbol: InjectionKey<VitePressData> = Symbol()
export interface VitePressData<T = any> {
site: Ref<SiteData<T>>
page: Ref<PageData>
theme: Ref<T>
frontmatter: Ref<PageData['frontmatter']>
title: Ref<string>
description: Ref<string>
lang: Ref<string>
localePath: Ref<string>
}
// site data is a singleton
export type SiteDataRef<T = any> = Ref<SiteData<T>>
export const siteDataRef: Ref<SiteData> = shallowRef(parse(serializedSiteData))
function parse(data: string): SiteData {
const parsed = JSON.parse(data)
return (import.meta.env.DEV ? readonly(parsed) : parsed) as SiteData
}
// hmr
if (import.meta.hot) {
import.meta.hot!.accept('/@siteData', (m) => {
siteDataRef.value = parse(m.default)
})
}
// per-app data
export function initData(route: Route): VitePressData {
const site = computed(() =>
resolveSiteDataByRoute(siteDataRef.value, route.path)
)
return {
site,
theme: computed(() => site.value.themeConfig),
page: computed(() => route.data),
frontmatter: computed(() => route.data.frontmatter),
lang: computed(() => site.value.lang),
localePath: computed(() => {
const { langs, lang } = site.value
const path = Object.keys(langs).find(
(langPath) => langs[langPath].lang === lang
)
return withBase(path || '/')
}),
title: computed(() => {
return route.data.title
? route.data.title + ' | ' + site.value.title
: site.value.title
}),
description: computed(() => {
return route.data.description || site.value.description
})
}
}
export function useData<T = any>(): VitePressData<T> {
const data = inject(dataSymbol)
if (!data) {
throw new Error('vitepress data not properly injected in app')
}
return data
}

@ -1,20 +1,40 @@
import 'vite/dynamic-import-polyfill'
import { App, createApp as createClientApp, createSSRApp, h } from 'vue'
import {
App,
createApp as createClientApp,
createSSRApp,
defineAsyncComponent,
h,
onMounted,
watch
} from 'vue'
import { inBrowser, pathToFile } from './utils'
import { Router, RouterSymbol, createRouter } from './router'
import { mixinGlobalComputed, mixinGlobalComponents } from './mixin'
import { siteDataRef } from './composables/siteData'
import { useSiteDataByRoute } from './composables/siteDataByRoute'
import { usePageData } from './composables/pageData'
import { siteDataRef, useData } from './data'
import { useUpdateHead } from './composables/head'
import Theme from '/@theme/index'
import { usePrefetch } from './composables/preFetch'
import { dataSymbol, initData } from './data'
import { Content } from './components/Content'
import { ClientOnly } from './components/ClientOnly'
const NotFound = Theme.NotFound || (() => '404 Not Found')
const VitePressApp = {
name: 'VitePressApp',
setup() {
const { site } = useData()
// change the language on the HTML element based on the current lang
onMounted(() => {
watch(
() => site.value.lang,
(lang: string) => {
document.documentElement.lang = lang
},
{ immediate: true }
)
})
if (import.meta.env.PROD) {
// in prod mode, enable intersectionObserver based pre-fetch
usePrefetch()
@ -32,16 +52,30 @@ export function createApp() {
app.provide(RouterSymbol, router)
const siteDataByRouteRef = useSiteDataByRoute(router.route)
const pageDataRef = usePageData(router.route)
const data = initData(router.route)
app.provide(dataSymbol, data)
if (inBrowser) {
// dynamically update head tags
useUpdateHead(router.route, siteDataByRouteRef)
useUpdateHead(router.route, data.site)
}
mixinGlobalComputed(app, siteDataRef, siteDataByRouteRef, pageDataRef)
mixinGlobalComponents(app)
// install global components
app.component('Content', Content)
app.component('ClientOnly', ClientOnly)
app.component(
'Debug',
import.meta.env.PROD
? () => null
: defineAsyncComponent(() => import('./components/Debug.vue'))
)
// expose $frontmatter
Object.defineProperty(app.config.globalProperties, '$frontmatter', {
get() {
return data.frontmatter.value
}
})
if (Theme.enhanceApp) {
Theme.enhanceApp({

@ -1,95 +0,0 @@
import { App, defineAsyncComponent } from 'vue'
import { joinPath } from './utils'
import { SiteDataRef } from './composables/siteData'
import { PageDataRef } from './composables/pageData'
import { Content } from './components/Content'
import { ClientOnly } from './components/ClientOnly'
export function mixinGlobalComputed(
app: App,
site: SiteDataRef,
siteByRoute: SiteDataRef,
page: PageDataRef
): void {
Object.defineProperties(app.config.globalProperties, {
$site: {
get() {
return site.value
}
},
$siteByRoute: {
get() {
return siteByRoute.value
}
},
$themeConfig: {
get() {
return siteByRoute.value.themeConfig
}
},
$page: {
get() {
return page.value
}
},
$frontmatter: {
get() {
return page.value.frontmatter
}
},
$lang: {
get() {
return siteByRoute.value.lang
}
},
$localePath: {
get() {
const { locales } = site.value
const { lang } = siteByRoute.value
const path = Object.keys(locales).find(
(lp) => locales[lp].lang === lang
)
return (locales && path) || '/'
}
},
$title: {
get() {
return page.value.title
? page.value.title + ' | ' + siteByRoute.value.title
: siteByRoute.value.title
}
},
$description: {
get() {
return page.value.description || siteByRoute.value.description
}
},
$withBase: {
value(path: string) {
return joinPath(site.value.base, path)
}
}
})
}
export function mixinGlobalComponents(app: App) {
app.component('Content', Content)
app.component('ClientOnly', ClientOnly)
app.component(
'Debug',
import.meta.env.PROD
? () => null
: defineAsyncComponent(() => import('./components/Debug.vue'))
)
}

@ -1,6 +1,8 @@
import { reactive, inject, markRaw, nextTick, readonly } from 'vue'
import type { Component, InjectionKey } from 'vue'
import { PageData } from '../../../types/shared'
import { PageData } from '../shared'
import { inBrowser } from './utils'
import { siteDataRef } from './data'
export interface Route {
path: string
@ -20,14 +22,20 @@ export const RouterSymbol: InjectionKey<Router> = Symbol()
// matter and is only passed to support same-host hrefs.
const fakeHost = `http://a.com`
const notFoundPageData: PageData = {
relativePath: '',
title: '404',
description: 'Not Found',
headers: [],
frontmatter: {},
lastUpdated: 0
}
const getDefaultRoute = (): Route => ({
path: '/',
hash: '',
component: null,
// this will be set upon initial page load, which is before
// the app is mounted, so it's guaranteed to be available in
// components
data: null as any
data: notFoundPageData
})
interface PageModule {
@ -40,7 +48,6 @@ export function createRouter(
fallbackComponent?: Component
): Router {
const route = reactive(getDefaultRoute())
const inBrowser = typeof window !== 'undefined'
function go(href: string = inBrowser ? location.href : '/') {
// ensure correct deep link so page refresh lands on correct files.
@ -59,7 +66,7 @@ export function createRouter(
let latestPendingPath: string | null = null
async function loadPage(href: string, scrollPosition = 0) {
async function loadPage(href: string, scrollPosition = 0, isRetry = false) {
const targetLoc = new URL(href, fakeHost)
const pendingPath = (latestPendingPath = targetLoc.pathname)
const pendingHash = decodeURIComponent(targetLoc.hash)
@ -81,12 +88,21 @@ export function createRouter(
route.path = pendingPath
route.hash = pendingHash
route.component = markRaw(comp)
route.data = readonly(JSON.parse(__pageData)) as PageData
route.data = import.meta.env.PROD
? markRaw(JSON.parse(__pageData))
: (readonly(JSON.parse(__pageData)) as PageData)
if (inBrowser) {
nextTick(() => {
if (pendingHash && !scrollPosition) {
const target = document.querySelector(pendingHash) as HTMLElement
let target: HTMLElement | null = null
try {
target = document.querySelector(
decodeURIComponent(pendingHash)
) as HTMLElement
} catch (e) {
console.warn(e)
}
if (target) {
scrollTo(target, pendingHash)
return
@ -96,15 +112,29 @@ export function createRouter(
})
}
}
} catch (err) {
} catch (err: any) {
if (!err.message.match(/fetch/)) {
console.error(err)
}
// retry on fetch fail: the page to hash map may have been invalidated
// because a new deploy happened while the page is open. Try to fetch
// the updated pageToHash map and fetch again.
if (!isRetry) {
try {
const res = await fetch(siteDataRef.value.base + 'hashmap.json')
;(window as any).__VP_HASH_MAP__ = await res.json()
await loadPage(href, scrollPosition, true)
return
} catch (e) {}
}
if (latestPendingPath === pendingPath) {
latestPendingPath = null
route.path = pendingPath
route.hash = pendingHash
route.component = fallbackComponent ? markRaw(fallbackComponent) : null
route.data = notFoundPageData
}
}
}
@ -132,10 +162,10 @@ export function createRouter(
e.preventDefault()
if (pathname === currentUrl.pathname) {
// scroll between hash anchors in the same page
if (hash) {
if (hash !== currentUrl.hash) {
history.pushState(null, '', hash)
}
if (hash && hash !== currentUrl.hash) {
history.pushState(null, '', hash)
// still emit the event so we can listen to it in themes
window.dispatchEvent(new Event('hashchange'))
// use smooth scroll when clicking on header anchor links
scrollTo(
link,
@ -180,14 +210,34 @@ export function useRoute(): Route {
return useRouter().route
}
function scrollTo(el: HTMLElement, selector: string, smooth = false) {
function scrollTo(el: HTMLElement, hash: string, smooth = false) {
let target: Element | null = null
const pageOffset = (document.querySelector('.nav-bar') as HTMLElement)
.offsetHeight
const target = el.classList.contains('.header-anchor')
? el
: document.querySelector(selector)
try {
target = el.classList.contains('header-anchor')
? el
: document.querySelector(decodeURIComponent(hash))
} catch (e) {
console.warn(e)
}
if (target) {
const targetTop = (target as HTMLElement).offsetTop - pageOffset - 15
let offset = siteDataRef.value.scrollOffset
if (typeof offset === 'string') {
offset =
document.querySelector(offset)!.getBoundingClientRect().bottom + 24
}
const targetPadding = parseInt(
window.getComputedStyle(target as HTMLElement).paddingTop,
10
)
const targetTop =
window.scrollY +
(target as HTMLElement).getBoundingClientRect().top -
offset +
targetPadding
// only smooth scroll if distance is smaller than screen height.
if (!smooth || Math.abs(targetTop - window.scrollY) > window.innerHeight) {
window.scrollTo(0, targetTop)

@ -1,6 +1,6 @@
import { App, Ref, ComponentOptions } from 'vue'
import { App, Ref, Component } from 'vue'
import { Router } from './router'
import { SiteData } from '../../../types/shared'
import { SiteData } from '../shared'
export interface EnhanceAppContext {
app: App
@ -9,7 +9,7 @@ export interface EnhanceAppContext {
}
export interface Theme {
Layout: ComponentOptions
NotFound?: ComponentOptions
Layout: Component
NotFound?: Component
enhanceApp?: (ctx: EnhanceAppContext) => void
}

@ -1,4 +1,7 @@
export const inBrowser = typeof window !== 'undefined'
import { siteDataRef } from './data'
import { inBrowser, EXTERNAL_URL_RE } from '../shared'
export { inBrowser }
/**
* Join two paths by resolving the slash collision.
@ -7,11 +10,18 @@ export function joinPath(base: string, path: string): string {
return `${base}${path}`.replace(/\/+/g, '/')
}
export function withBase(path: string) {
return EXTERNAL_URL_RE.test(path)
? path
: joinPath(siteDataRef.value.base, path)
}
/**
* Converts a url path to the corresponding js chunk filename.
*/
export function pathToFile(path: string): string {
let pagePath = path.replace(/\.html$/, '')
pagePath = decodeURIComponent(pagePath)
if (pagePath.endsWith('/')) {
pagePath += 'index'
}

@ -3,19 +3,24 @@
// generic types
export type { Router, Route } from './app/router'
export type { VitePressData } from './app/data'
// theme types
export type { Theme, EnhanceAppContext } from './app/theme'
// shared types
export type {
PageData,
SiteData,
HeadConfig,
Header,
LocaleConfig
} from '../../types/shared'
// composables
export { useData } from './app/data'
export { useRouter, useRoute } from './app/router'
export { useSiteData } from './app/composables/siteData'
export { useSiteDataByRoute } from './app/composables/siteDataByRoute'
export { usePageData } from './app/composables/pageData'
export { useFrontmatter } from './app/composables/frontmatter'
// utilities
export { inBrowser, joinPath } from './app/utils'
export { inBrowser, withBase } from './app/utils'
// components
export { Content } from './app/components/Content'

@ -1,89 +1,14 @@
<template>
<div class="theme" :class="pageClasses">
<NavBar v-if="showNavbar" @toggle="toggleSidebar">
<template #search>
<slot name="navbar-search">
<AlgoliaSearchBox v-if="theme.algolia" :options="theme.algolia" />
</slot>
</template>
</NavBar>
<SideBar :open="openSideBar">
<template #sidebar-top>
<slot name="sidebar-top" />
</template>
<template #sidebar-bottom>
<slot name="sidebar-bottom" />
</template>
</SideBar>
<!-- TODO: make this button accessible -->
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<Content v-if="isCustomLayout" />
<Home v-else-if="enableHome">
<template #hero>
<slot name="home-hero" />
</template>
<template #features>
<slot name="home-features" />
</template>
<template #footer>
<slot name="home-footer" />
</template>
</Home>
<Page v-else>
<template #top>
<slot name="page-top-ads">
<div
id="ads-container"
v-if="theme.carbonAds && theme.carbonAds.carbon"
>
<CarbonAds
:key="'carbon' + page.relativePath"
:code="theme.carbonAds.carbon"
:placement="theme.carbonAds.placement"
/>
</div>
</slot>
<slot name="page-top" />
</template>
<template #bottom>
<slot name="page-bottom" />
<slot name="page-bottom-ads">
<BuySellAds
v-if="theme.carbonAds && theme.carbonAds.custom"
:key="'custom' + page.relativePath"
:code="theme.carbonAds.custom"
:placement="theme.carbonAds.placement"
/>
</slot>
</template>
</Page>
</div>
<Debug />
</template>
<script setup lang="ts">
import { ref, computed, watch, defineAsyncComponent } from 'vue'
import {
useRoute,
useSiteData,
usePageData,
useSiteDataByRoute
} from 'vitepress'
import { useRoute, useData } from 'vitepress'
import { isSideBarEmpty, getSideBarConfig } from './support/sideBar'
import type { DefaultTheme } from './config'
// components
import Home from './components/Home.vue'
import NavBar from './components/NavBar.vue'
import SideBar from './components/SideBar.vue'
import Page from './components/Page.vue'
const Home = defineAsyncComponent(() => import('./components/Home.vue'))
const NoopComponent = () => null
const CarbonAds = __CARBON__
@ -98,28 +23,24 @@ const AlgoliaSearchBox = __ALGOLIA__
// generic state
const route = useRoute()
const siteData = useSiteData<DefaultTheme.Config>()
const siteRouteData = useSiteDataByRoute()
const theme = computed(() => siteData.value.themeConfig)
const page = usePageData()
const { site, page, theme, frontmatter } = useData()
// custom layout
const isCustomLayout = computed(() => !!route.data.frontmatter.customLayout)
const isCustomLayout = computed(() => !!frontmatter.value.customLayout)
// home
const enableHome = computed(() => !!route.data.frontmatter.home)
const enableHome = computed(() => !!frontmatter.value.home)
// automatic multilang check for AlgoliaSearchBox
const isMultiLang = computed(() => Object.keys(site.value.langs).length > 1)
// navbar
const showNavbar = computed(() => {
const { themeConfig } = siteRouteData.value
const { frontmatter } = route.data
if (frontmatter.navbar === false || themeConfig.navbar === false) {
const themeConfig = theme.value
if (frontmatter.value.navbar === false || themeConfig.navbar === false) {
return false
}
return (
siteData.value.title ||
themeConfig.logo ||
themeConfig.repo ||
themeConfig.nav
site.value.title || themeConfig.logo || themeConfig.repo || themeConfig.nav
)
})
@ -127,16 +48,12 @@ const showNavbar = computed(() => {
const openSideBar = ref(false)
const showSidebar = computed(() => {
const { frontmatter } = route.data
if (frontmatter.home || frontmatter.sidebar === false) {
if (frontmatter.value.home || frontmatter.value.sidebar === false) {
return false
}
const { themeConfig } = siteRouteData.value
return !isSideBarEmpty(
getSideBarConfig(themeConfig.sidebar, route.data.relativePath)
getSideBarConfig(theme.value.sidebar, route.data.relativePath)
)
})
@ -162,6 +79,83 @@ const pageClasses = computed(() => {
})
</script>
<template>
<div class="theme" :class="pageClasses">
<NavBar v-if="showNavbar" @toggle="toggleSidebar">
<template #search>
<slot name="navbar-search">
<AlgoliaSearchBox
v-if="theme.algolia"
:options="theme.algolia"
:multilang="isMultiLang"
/>
</slot>
</template>
</NavBar>
<SideBar :open="openSideBar">
<template #sidebar-top>
<slot name="sidebar-top" />
</template>
<template #sidebar-bottom>
<slot name="sidebar-bottom" />
</template>
</SideBar>
<!-- TODO: make this button accessible -->
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<Content v-if="isCustomLayout" />
<template v-else-if="enableHome">
<!-- A slot for customizing the entire homepage easily -->
<slot name="home">
<Home>
<template #hero>
<slot name="home-hero" />
</template>
<template #features>
<slot name="home-features" />
</template>
<template #footer>
<slot name="home-footer" />
</template>
</Home>
</slot>
</template>
<Page v-else>
<template #top>
<slot name="page-top-ads">
<div
id="ads-container"
v-if="theme.carbonAds && theme.carbonAds.carbon"
>
<CarbonAds
:key="'carbon' + page.relativePath"
:code="theme.carbonAds.carbon"
:placement="theme.carbonAds.placement"
/>
</div>
</slot>
<slot name="page-top" />
</template>
<template #bottom>
<slot name="page-bottom" />
<slot name="page-bottom-ads">
<BuySellAds
v-if="theme.carbonAds && theme.carbonAds.custom"
:key="'custom' + page.relativePath"
:code="theme.carbonAds.custom"
:placement="theme.carbonAds.placement"
/>
</slot>
</template>
</Page>
</div>
<Debug />
</template>
<style>
#ads-container {
margin: 0 auto;

@ -2,11 +2,14 @@
<div class="theme">
<h1>404</h1>
<blockquote>{{ getMsg() }}</blockquote>
<a :href="$site.base" aria-label="go to home">Take me home.</a>
<a :href="site.base" aria-label="go to home">Take me home.</a>
</div>
</template>
<script setup lang="ts">
import { useData } from 'vitepress'
const { site } = useData()
const msgs = [
`There's nothing here.`,
`How did we get here?`,

@ -1,17 +1,14 @@
<template>
<div class="algolia-search-box" id="docsearch" />
</template>
<script setup lang="ts">
import '@docsearch/css'
import { useRoute, useRouter } from 'vitepress'
import { defineProps, getCurrentInstance, onMounted, watch } from 'vue'
import docsearch from '@docsearch/js'
import { useRoute, useRouter, useData } from 'vitepress'
import { getCurrentInstance, onMounted, watch } from 'vue'
import type { DefaultTheme } from '../config'
import type { DocSearchHit } from '@docsearch/react/dist/esm/types'
const props = defineProps<{
options: DefaultTheme.AlgoliaSearchOptions
multilang?: boolean
}>()
const vm = getCurrentInstance()
@ -53,25 +50,46 @@ function update(options: any) {
}
}
const { lang } = useData()
// if the user has multiple locales, the search results should be filtered
// based on the language
const facetFilters: string[] = props.multilang ? ['lang:' + lang.value] : []
if (props.options.searchParameters?.facetFilters) {
facetFilters.push(...props.options.searchParameters.facetFilters)
}
watch(lang, (newLang, oldLang) => {
const index = facetFilters.findIndex((filter) => filter === 'lang:' + oldLang)
if (index > -1) {
facetFilters.splice(index, 1, 'lang:' + newLang)
}
})
function initialize(userOptions: any) {
docsearch(
Object.assign({}, userOptions, {
container: '#docsearch',
searchParameters: Object.assign({}, userOptions.searchParameters),
searchParameters: Object.assign({}, userOptions.searchParameters, {
// pass a custom lang facetFilter to allow multiple language search
// https://github.com/algolia/docsearch-configs/pull/3942
facetFilters
}),
navigator: {
navigate: ({ suggestionUrl }: { suggestionUrl: string }) => {
navigate: ({ itemUrl }: { itemUrl: string }) => {
const { pathname: hitPathname } = new URL(
window.location.origin + suggestionUrl
window.location.origin + itemUrl
)
// Router doesn't handle same-page navigation so we use the native
// browser location API for anchor navigation
if (route.path === hitPathname) {
window.location.assign(window.location.origin + suggestionUrl)
window.location.assign(window.location.origin + itemUrl)
} else {
router.go(suggestionUrl)
router.go(itemUrl)
}
}
},
@ -123,7 +141,8 @@ function initialize(userOptions: any) {
router.go(relativeHit)
},
children
}
},
__v: null
}
}
})
@ -131,6 +150,10 @@ function initialize(userOptions: any) {
}
</script>
<template>
<div class="algolia-search-box" id="docsearch" />
</template>
<style>
.algolia-search-box {
padding-top: 1px;
@ -139,13 +162,12 @@ function initialize(userOptions: any) {
@media (min-width: 720px) {
.algolia-search-box {
padding-left: 8px;
min-width: 176.3px; /* avoid layout shift */
}
}
@media (min-width: 751px) {
.algolia-search-box {
padding-left: 8px;
min-width: 176.3px; /* avoid layout shift */
}
.algolia-search-box .DocSearch-Button-Placeholder {

@ -1,11 +1,5 @@
<template>
<div class="buy-sell-ads">
<div class="bsa-cpc" />
</div>
</template>
<script setup lang="ts">
import { defineProps, onMounted } from 'vue'
import { onMounted } from 'vue'
// global _bsa
const ID = 'bsa-cpc-script'
@ -51,6 +45,10 @@ onMounted(() => {
function load() {
if (typeof _bsa !== 'undefined' && _bsa) {
const parent = document.querySelector('.bsa-cpc')!
// cleanup any existing ad to avoid them stacking
parent.innerHTML = ''
_bsa.init('default', code, `placement:${placement}`, {
target: '.bsa-cpc',
align: 'horizontal',
@ -60,6 +58,12 @@ function load() {
}
</script>
<template>
<div class="buy-sell-ads">
<div class="bsa-cpc" />
</div>
</template>
<style scoped>
.buy-sell-ads {
margin: 0 auto;

@ -1,9 +1,5 @@
<template>
<div class="carbon-ads" ref="el" />
</template>
<script setup lang="ts">
import { defineProps, ref, onMounted } from 'vue'
import { ref, onMounted } from 'vue'
const { code, placement } = defineProps<{
code: string
@ -20,10 +16,15 @@ onMounted(() => {
})
</script>
<template>
<div class="carbon-ads" ref="el" />
</template>
<style scoped>
.carbon-ads {
border-radius: 4px;
margin: 0 auto;
padding: 8px;
max-width: 280px;
font-size: 0.75rem;
background-color: var(--c-bg-accent);
@ -41,7 +42,6 @@ onMounted(() => {
z-index: 1;
float: right;
margin: -8px -8px 24px 24px;
padding: 8px;
width: 146px;
max-width: 100%;
min-height: 200px;

@ -1,3 +1,10 @@
<script setup lang="ts">
import { useEditLink } from '../composables/editLink'
import OutboundLink from './icons/OutboundLink.vue'
const { url, text } = useEditLink()
</script>
<template>
<div class="edit-link">
<a
@ -12,13 +19,6 @@
</div>
</template>
<script setup lang="ts">
import { useEditLink } from '../composables/editLink'
import OutboundLink from './icons/OutboundLink.vue'
const { url, text } = useEditLink()
</script>
<style scoped>
.link {
display: inline-block;

@ -1,3 +1,9 @@
<script setup lang="ts">
import HomeHero from './HomeHero.vue'
import HomeFeatures from './HomeFeatures.vue'
import HomeFooter from './HomeFooter.vue'
</script>
<template>
<main class="home" aria-labelledby="main-title">
<HomeHero />
@ -12,12 +18,6 @@
</main>
</template>
<script setup lang="ts">
import HomeHero from './HomeHero.vue'
import HomeFeatures from './HomeFeatures.vue'
import HomeFooter from './HomeFooter.vue'
</script>
<style scoped>
.home {
padding-top: var(--header-height);
@ -28,11 +28,4 @@ import HomeFooter from './HomeFooter.vue'
margin: 0px auto;
padding: 0 1.5rem;
}
@media (max-width: 720px) {
.home-content {
max-width: 392px;
padding: 0;
}
}
</style>

@ -1,3 +1,23 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress'
const { frontmatter } = useData()
const hasFeatures = computed(() => {
return frontmatter.value.features && frontmatter.value.features.length > 0
})
interface Feature {
title?: string
details?: string
}
const features = computed<Feature[]>(() => {
return frontmatter.value.features ? frontmatter.value.features : []
})
</script>
<template>
<div v-if="hasFeatures" class="home-features">
<div class="wrapper">
@ -17,21 +37,6 @@
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSiteDataByRoute, useFrontmatter } from 'vitepress'
const data = useFrontmatter()
const hasFeatures = computed(() => {
return data.value.features && data.value.features.length > 0
})
const features = computed(() => {
return data.value.features ? data.value.features : []
})
</script>
<style scoped>
.home-features {
margin: 0 auto;

@ -1,7 +1,13 @@
<script setup lang="ts">
import { useData } from 'vitepress'
const { frontmatter } = useData()
</script>
<template>
<footer v-if="$frontmatter.footer" class="footer">
<footer v-if="frontmatter.footer" class="footer">
<div class="container">
<p class="text">{{ $frontmatter.footer }}</p>
<p class="text">{{ frontmatter.footer }}</p>
</div>
</footer>
</template>

@ -1,59 +1,52 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useData, withBase } from 'vitepress'
import NavLink from './NavLink.vue'
const { site, frontmatter } = useData()
const showHero = computed(() => {
const { heroImage, heroText, tagline, actionLink, actionText } =
frontmatter.value
return heroImage || heroText || tagline || (actionLink && actionText)
})
const heroText = computed(() => frontmatter.value.heroText || site.value.title)
const tagline = computed(
() => frontmatter.value.tagline || site.value.description
)
</script>
<template>
<header v-if="showHero" class="home-hero">
<figure v-if="$frontmatter.heroImage" class="figure">
<figure v-if="frontmatter.heroImage" class="figure">
<img
class="image"
:src="$withBase($frontmatter.heroImage)"
:alt="$frontmatter.heroAlt"
:src="withBase(frontmatter.heroImage)"
:alt="frontmatter.heroAlt"
/>
</figure>
<h1 v-if="hasHeroText" id="main-title" class="title">{{ heroText }}</h1>
<p v-if="hasTagline" class="description">{{ tagline }}</p>
<h1 v-if="heroText" id="main-title" class="title">{{ heroText }}</h1>
<p v-if="tagline" class="tagline">{{ tagline }}</p>
<NavLink
v-if="hasAction"
:item="{ link: data.actionLink, text: data.actionText }"
v-if="frontmatter.actionLink && frontmatter.actionText"
:item="{ link: frontmatter.actionLink, text: frontmatter.actionText }"
class="action"
/>
<NavLink
v-if="hasAltAction"
:item="{ link: data.altActionLink, text: data.altActionText }"
v-if="frontmatter.altActionLink && frontmatter.altActionText"
:item="{
link: frontmatter.altActionLink,
text: frontmatter.altActionText
}"
class="action alt"
/>
</header>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSiteDataByRoute, useFrontmatter } from 'vitepress'
import NavLink from './NavLink.vue'
const site = useSiteDataByRoute()
const data = useFrontmatter()
const showHero = computed(() => {
return (
data.value.heroImage ||
hasHeroText.value ||
hasTagline.value ||
hasAction.value
)
})
const hasHeroText = computed(() => data.value.heroText !== null)
const heroText = computed(() => data.value.heroText || site.value.title)
const hasTagline = computed(() => data.value.tagline !== null)
const tagline = computed(() => data.value.tagline || site.value.description)
const hasAction = computed(() => data.value.actionLink && data.value.actionText)
const hasAltAction = computed(
() => data.value.altActionLink && data.value.altActionText
)
</script>
<style scoped>
.home-hero {
margin: 2.5rem 0 2.75rem;
@ -102,7 +95,7 @@ const hasAltAction = computed(
}
}
.description {
.tagline {
margin: 0;
margin-top: 0.25rem;
line-height: 1.3;
@ -111,7 +104,7 @@ const hasAltAction = computed(
}
@media (min-width: 420px) {
.description {
.tagline {
line-height: 1.2;
font-size: 1.6rem;
}

@ -1,36 +1,37 @@
<template>
<p v-if="hasLastUpdated" class="last-updated">
<span class="prefix">{{ prefix }}:</span>
<span class="datetime">{{ datetime }}</span>
</p>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useSiteDataByRoute, usePageData } from 'vitepress'
import { ref, computed, onMounted, watchEffect } from 'vue'
import { useData } from 'vitepress'
const site = useSiteDataByRoute()
const page = usePageData()
const { theme, page } = useData()
const hasLastUpdated = computed(() => {
const lu = site.value.themeConfig.lastUpdated
const lu = theme.value.lastUpdated
return lu !== undefined && lu !== false
return lu !== undefined && lu !== false && page.value.lastUpdated !== 0
})
const prefix = computed(() => {
const p = site.value.themeConfig.lastUpdated
const p = theme.value.lastUpdated
return p === true ? 'Last Updated' : p
})
const datetime = ref('')
onMounted(() => {
// locale string might be different based on end user
// and will lead to potential hydration mismatch if calculated at build time
datetime.value = new Date(page.value.lastUpdated).toLocaleString('en-US')
watchEffect(() => {
// locale string might be different based on end user
// and will lead to potential hydration mismatch if calculated at build time
datetime.value = new Date(page.value.lastUpdated!).toLocaleString('en-US')
})
})
</script>
<template>
<p v-if="hasLastUpdated" class="last-updated">
<span class="prefix">{{ prefix }}:</span>
<span class="datetime">{{ datetime }}</span>
</p>
</template>
<style scoped>
.last-updated {
display: inline-block;

@ -1,3 +1,11 @@
<script setup lang="ts">
import NavBarTitle from './NavBarTitle.vue'
import NavLinks from './NavLinks.vue'
import ToggleSideBarButton from './ToggleSideBarButton.vue'
defineEmits(['toggle'])
</script>
<template>
<header class="nav-bar">
<ToggleSideBarButton @toggle="$emit('toggle')" />
@ -14,15 +22,6 @@
</header>
</template>
<script setup lang="ts">
import { defineEmit } from 'vue'
import NavBarTitle from './NavBarTitle.vue'
import NavLinks from './NavLinks.vue'
import ToggleSideBarButton from './ToggleSideBarButton.vue'
defineEmit(['toggle'])
</script>
<style scoped>
.nav-bar {
position: fixed;

@ -1,16 +1,21 @@
<script setup lang="ts">
import { withBase, useData } from 'vitepress'
const { site, theme, localePath } = useData()
</script>
<template>
<a
class="nav-bar-title"
:href="$withBase($localePath)"
:aria-label="`${$siteByRoute.title}, back to home`"
:href="localePath"
:aria-label="`${site.title}, back to home`"
>
<img
v-if="$themeConfig.logo"
v-if="theme.logo"
class="logo"
:src="$withBase($themeConfig.logo)"
:src="withBase(theme.logo)"
alt="Logo"
/>
{{ $site.title }}
{{ site.title }}
</a>
</template>
@ -19,6 +24,9 @@
font-size: 1.3rem;
font-weight: 600;
color: var(--c-text);
display: flex;
justify-content: center;
align-items: center;
}
.nav-bar-title:hover {

@ -1,20 +1,5 @@
<template>
<div class="nav-dropdown-link" :class="{ open }">
<button class="button" :aria-label="item.ariaLabel" @click="toggle">
<span class="button-text">{{ item.text }}</span>
<span class="button-arrow" :class="open ? 'down' : 'right'" />
</button>
<ul class="dialog">
<li v-for="item in item.items" :key="item.text" class="dialog-item">
<NavDropdownLinkItem :item="item" />
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue'
import { ref, watch } from 'vue'
import { useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'
import NavDropdownLinkItem from './NavDropdownLinkItem.vue'
@ -39,6 +24,21 @@ function toggle() {
}
</script>
<template>
<div class="nav-dropdown-link" :class="{ open }">
<button class="button" :aria-label="item.ariaLabel" @click="toggle">
<span class="button-text">{{ item.text }}</span>
<span class="button-arrow" :class="open ? 'down' : 'right'" />
</button>
<ul class="dialog">
<li v-for="item in item.items" :key="item.text" class="dialog-item">
<NavDropdownLinkItem :item="item" />
</li>
</ul>
</div>
</template>
<style scoped>
.nav-dropdown-link {
position: relative;

@ -1,15 +1,5 @@
<template>
<div class="nav-dropdown-link-item">
<a class="item" v-bind="linkProps">
<span class="arrow" />
<span class="text">{{ item.text }}</span>
<span class="icon"><OutboundLink v-if="isExternal" /></span>
</a>
</div>
</template>
<script setup lang="ts">
import { defineProps, toRefs } from 'vue'
import { toRefs } from 'vue'
import type { DefaultTheme } from '../config'
import { useNavLink } from '../composables/navLink'
import OutboundLink from './icons/OutboundLink.vue'
@ -23,6 +13,16 @@ const propsRefs = toRefs(props)
const { props: linkProps, isExternal } = useNavLink(propsRefs.item)
</script>
<template>
<div class="nav-dropdown-link-item">
<a class="item" v-bind="linkProps">
<span class="arrow" />
<span class="text">{{ item.text }}</span>
<span class="icon"><OutboundLink v-if="isExternal" /></span>
</a>
</div>
</template>
<style scoped>
.item {
display: block;

@ -1,13 +1,5 @@
<template>
<div class="nav-link">
<a class="item" v-bind="linkProps">
{{ item.text }} <OutboundLink v-if="isExternal" />
</a>
</div>
</template>
<script setup lang="ts">
import { defineProps, toRefs } from 'vue'
import { toRefs } from 'vue'
import type { DefaultTheme } from '../config'
import { useNavLink } from '../composables/navLink'
import OutboundLink from './icons/OutboundLink.vue'
@ -21,6 +13,14 @@ const propsRefs = toRefs(props)
const { props: linkProps, isExternal } = useNavLink(propsRefs.item)
</script>
<template>
<div class="nav-link">
<a class="item" v-bind="linkProps">
{{ item.text }} <OutboundLink v-if="isExternal" />
</a>
</div>
</template>
<style scoped>
.item {
display: block;

@ -1,7 +1,21 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useData } from 'vitepress'
import { useLanguageLinks } from '../composables/nav'
import { useRepo } from '../composables/repo'
import NavLink from './NavLink.vue'
import NavDropdownLink from './NavDropdownLink.vue'
const { theme } = useData()
const localeLinks = useLanguageLinks()
const repo = useRepo()
const show = computed(() => theme.value.nav || repo.value || localeLinks.value)
</script>
<template>
<nav v-if="show" class="nav-links">
<template v-if="links">
<div v-for="item in links" :key="item.text" class="item">
<template v-if="theme.nav">
<div v-for="item in theme.nav" :key="item.text" class="item">
<NavDropdownLink v-if="item.items" :item="item" />
<NavLink v-else :item="item" />
</div>
@ -17,23 +31,6 @@
</nav>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useSiteDataByRoute } from 'vitepress'
import { useLocaleLinks } from '../composables/nav'
import { useRepo } from '../composables/repo'
import NavLink from './NavLink.vue'
import NavDropdownLink from './NavDropdownLink.vue'
const site = useSiteDataByRoute()
const localeLinks = useLocaleLinks()
const repo = useRepo()
const show = computed(() => links.value || repo.value)
const links = computed(() => site.value.themeConfig.nav)
</script>
<style scoped>
.nav-links {
padding: 0.75rem 0;

@ -1,14 +1,23 @@
<script setup lang="ts">
import { withBase } from 'vitepress'
import { useNextAndPrevLinks } from '../composables/nextAndPrevLinks'
import ArrowLeft from './icons/ArrowLeft.vue'
import ArrowRight from './icons/ArrowRight.vue'
const { hasLinks, prev, next } = useNextAndPrevLinks()
</script>
<template>
<div v-if="hasLinks" class="next-and-prev-link">
<div class="container">
<div class="prev">
<a v-if="prev" class="link" :href="$withBase(prev.link)">
<a v-if="prev" class="link" :href="withBase(prev.link)">
<ArrowLeft class="icon icon-prev" />
<span class="text">{{ prev.text }}</span>
</a>
</div>
<div class="next">
<a v-if="next" class="link" :href="$withBase(next.link)">
<a v-if="next" class="link" :href="withBase(next.link)">
<span class="text">{{ next.text }}</span>
<ArrowRight class="icon icon-next" />
</a>
@ -17,14 +26,6 @@
</div>
</template>
<script setup lang="ts">
import { useNextAndPrevLinks } from '../composables/nextAndPrevLinks'
import ArrowLeft from './icons/ArrowLeft.vue'
import ArrowRight from './icons/ArrowRight.vue'
const { hasLinks, prev, next } = useNextAndPrevLinks()
</script>
<style scoped>
.next-and-prev-link {
padding-top: 1rem;

@ -1,14 +1,15 @@
<script setup lang="ts">
import PageFooter from './PageFooter.vue'
import NextAndPrevLinks from './NextAndPrevLinks.vue'
</script>
<template>
<main class="page">
<div class="container">
<slot name="top" />
<div class="content">
<Content />
</div>
<Content class="content" />
<PageFooter />
<NextAndPrevLinks />
<slot name="bottom" />
@ -16,11 +17,6 @@
</main>
</template>
<script setup lang="ts">
import PageFooter from './PageFooter.vue'
import NextAndPrevLinks from './NextAndPrevLinks.vue'
</script>
<style scoped>
.page {
padding-top: var(--header-height);

@ -1,19 +1,22 @@
<script setup lang="ts">
import EditLink from './EditLink.vue'
import LastUpdated from './LastUpdated.vue'
import { useData } from 'vitepress'
const { page } = useData()
</script>
<template>
<footer class="page-footer">
<div class="edit">
<EditLink />
</div>
<div class="updated">
<LastUpdated />
<LastUpdated v-if="page.lastUpdated" />
</div>
</footer>
</template>
<script setup lang="ts">
import EditLink from './EditLink.vue'
import LastUpdated from './LastUpdated.vue'
</script>
<style scoped>
.page-footer {
padding-top: 1rem;

@ -1,3 +1,10 @@
<script setup lang="ts">
import NavLinks from './NavLinks.vue'
import SideBarLinks from './SideBarLinks.vue'
defineProps<{ open: boolean }>()
</script>
<template>
<aside class="sidebar" :class="{ open }">
<NavLinks class="nav" />
@ -10,16 +17,6 @@
</aside>
</template>
<script setup lang="ts">
import { defineProps } from 'vue'
import NavLinks from './NavLinks.vue'
import SideBarLinks from './SideBarLinks.vue'
defineProps({
open: { type: Boolean, required: true }
})
</script>
<style scoped>
.sidebar {
position: fixed;

@ -1,6 +1,6 @@
import { FunctionalComponent, h, VNode } from 'vue'
import { useRoute, useSiteData } from 'vitepress'
import { Header } from '/@types/shared'
import { useRoute, useData } from 'vitepress'
import { Header } from '../../shared'
import { DefaultTheme } from '../config'
import { joinUrl, isActive } from '../utils'
@ -10,16 +10,22 @@ interface HeaderWithChildren extends Header {
export const SideBarLink: FunctionalComponent<{
item: DefaultTheme.SideBarItem
depth?: number
}> = (props) => {
const route = useRoute()
const site = useSiteData()
const { site, frontmatter } = useData()
const depth = props.depth || 1
const maxDepth = frontmatter.value.sidebarDepth || Infinity
const headers = route.data.headers
const text = props.item.text
const link = resolveLink(site.value.base, props.item.link)
const children = (props.item as DefaultTheme.SideBarGroup).children
const active = isActive(route, props.item.link)
const childItems = createChildren(active, children, headers)
const childItems =
depth < maxDepth
? createChildren(active, children, headers, depth + 1)
: null
return h('li', { class: 'sidebar-link' }, [
h(
@ -50,20 +56,21 @@ function resolveLink(base: string, path?: string): string | undefined {
function createChildren(
active: boolean,
children?: DefaultTheme.SideBarItem[],
headers?: Header[]
headers?: Header[],
depth = 1
): VNode | null {
if (children && children.length > 0) {
return h(
'ul',
{ class: 'sidebar-links' },
children.map((c) => {
return h(SideBarLink, { item: c })
return h(SideBarLink, { item: c, depth })
})
)
}
return active && headers
? createChildren(false, resolveHeaders(headers))
? createChildren(false, resolveHeaders(headers), undefined, depth)
: null
}

@ -1,12 +1,12 @@
<template>
<ul v-if="items.length > 0" class="sidebar-links">
<SideBarLink v-for="item of items" :key="item.text" :item="item" />
</ul>
</template>
<script setup lang="ts">
import { useSideBar } from '../composables/sideBar'
import { SideBarLink } from './SideBarLink'
const items = useSideBar()
</script>
<template>
<ul v-if="items.length > 0" class="sidebar-links">
<SideBarLink v-for="item of items" :item="item" />
</ul>
</template>

@ -1,3 +1,9 @@
<script lang="ts">
export default {
emits: ['toggle']
}
</script>
<template>
<div class="sidebar-button" @click="$emit('toggle')">
<svg
@ -16,12 +22,6 @@
</div>
</template>
<script>
export default {
emits: ['toggle']
}
</script>
<style>
.sidebar-button {
position: absolute;

@ -1,4 +1,4 @@
<template functional>
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M19,11H7.4l5.3-5.3c0.4-0.4,0.4-1,0-1.4s-1-0.4-1.4,0l-7,7c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.2-0.1,0.5,0,0.8c0.1,0.1,0.1,0.2,0.2,0.3l7,7c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3c0.4-0.4,0.4-1,0-1.4L7.4,13H19c0.6,0,1-0.4,1-1S19.6,11,19,11z"

@ -1,4 +1,4 @@
<template functional>
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M19.9,12.4c0.1-0.2,0.1-0.5,0-0.8c-0.1-0.1-0.1-0.2-0.2-0.3l-7-7c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l5.3,5.3H5c-0.6,0-1,0.4-1,1s0.4,1,1,1h11.6l-5.3,5.3c-0.4,0.4-0.4,1,0,1.4c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3l7-7C19.8,12.6,19.9,12.5,19.9,12.4z"

@ -1,4 +1,4 @@
<template functional>
<template>
<svg
class="icon outbound"
xmlns="http://www.w3.org/2000/svg"

@ -1,25 +1,25 @@
import { computed } from 'vue'
import { useSiteDataByRoute, usePageData } from 'vitepress'
import { endingSlashRE, isNullish, isExternal } from '../utils'
import { useData } from 'vitepress'
import { endingSlashRE, isExternal } from '../utils'
const bitbucketRE = /bitbucket.org/
export function useEditLink() {
const site = useSiteDataByRoute()
const page = usePageData()
const { page, theme, frontmatter } = useData()
const url = computed(() => {
const showEditLink = isNullish(page.value.frontmatter.editLink)
? site.value.themeConfig.editLinks
: page.value.frontmatter.editLink
const {
repo,
docsDir = '',
docsBranch = 'master',
docsRepo = repo
} = site.value.themeConfig
docsRepo = repo,
editLinks
} = theme.value
const showEditLink =
frontmatter.value.editLink != null
? frontmatter.value.editLink
: editLinks
const { relativePath } = page.value
if (!showEditLink || !relativePath || !repo) {
@ -30,7 +30,7 @@ export function useEditLink() {
})
const text = computed(() => {
return site.value.themeConfig.editLinkText || 'Edit this page'
return theme.value.editLinkText || 'Edit this page'
})
return {

@ -1,57 +1,30 @@
import { computed } from 'vue'
import { useRoute, useSiteData, inBrowser } from 'vitepress'
import { useData, useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'
export function useLocaleLinks() {
const route = useRoute()
const site = useSiteData()
export function useLanguageLinks() {
const { site, localePath, theme } = useData()
return computed(() => {
const theme = site.value.themeConfig as DefaultTheme.Config
const locales = theme.locales
const langs = site.value.langs
const localePaths = Object.keys(langs)
if (!locales) {
// one language
if (localePaths.length < 2) {
return null
}
const localeKeys = Object.keys(locales)
const route = useRoute()
if (localeKeys.length <= 1) {
return null
}
// handle site base
const siteBase = inBrowser ? site.value.base : '/'
const siteBaseWithoutSuffix = siteBase.endsWith('/')
? siteBase.slice(0, -1)
: siteBase
// remove site base in browser env
const routerPath = route.path.slice(siteBaseWithoutSuffix.length)
const currentLangBase = localeKeys.find((key) => {
return key === '/' ? false : routerPath.startsWith(key)
})
const currentContentPath = currentLangBase
? routerPath.substring(currentLangBase.length - 1)
: routerPath
const candidates = localeKeys.map((v) => {
const localePath = v.endsWith('/') ? v.slice(0, -1) : v
return {
text: locales[v].label,
link: `${localePath}${currentContentPath}`
}
})
// intentionally remove the leading slash because each locale has one
const currentPath = route.path.replace(localePath.value, '')
const currentLangKey = currentLangBase ? currentLangBase : '/'
const candidates = localePaths.map((localePath) => ({
text: langs[localePath].label,
link: `${localePath}${currentPath}`
}))
const selectText = locales[currentLangKey].selectText
? locales[currentLangKey].selectText
: 'Languages'
const selectText = theme.value.selectText || 'Languages'
return {
text: selectText,

@ -1,12 +1,10 @@
import { computed, Ref } from 'vue'
import { useRoute } from 'vitepress'
import type { DefaultTheme } from '../config'
import { useRoute, withBase } from 'vitepress'
import { isExternal as isExternalCheck } from '../utils'
import { useUrl } from '../composables/url'
import type { DefaultTheme } from '../config'
export function useNavLink(item: Ref<DefaultTheme.NavItemWithLink>) {
const route = useRoute()
const { withBase } = useUrl()
const isExternal = isExternalCheck(item.value.link)
@ -17,7 +15,7 @@ export function useNavLink(item: Ref<DefaultTheme.NavItemWithLink>) {
if (item.value.activeMatch) {
active = new RegExp(item.value.activeMatch).test(routePath)
} else {
const itemPath = normalizePath(withBase(item.value.link))
const itemPath = normalizePath(item.value.link)
active =
itemPath === '/'
? itemPath === routePath
@ -30,8 +28,8 @@ export function useNavLink(item: Ref<DefaultTheme.NavItemWithLink>) {
isExternal
},
href: isExternal ? item.value.link : withBase(item.value.link),
target: item.value.target || isExternal ? `_blank` : null,
rel: item.value.rel || isExternal ? `noopener noreferrer` : null,
target: item.value.target || (isExternal ? `_blank` : null),
rel: item.value.rel || (isExternal ? `noopener noreferrer` : null),
'aria-label': item.value.ariaLabel
}
})

@ -1,18 +1,17 @@
import { computed } from 'vue'
import { useSiteDataByRoute, usePageData } from 'vitepress'
import { useData } from 'vitepress'
import { isArray, ensureStartingSlash, removeExtention } from '../utils'
import { getSideBarConfig, getFlatSideBarLinks } from '../support/sideBar'
export function useNextAndPrevLinks() {
const site = useSiteDataByRoute()
const page = usePageData()
const { page, theme } = useData()
const path = computed(() => {
return removeExtention(ensureStartingSlash(page.value.relativePath))
})
const candidates = computed(() => {
const config = getSideBarConfig(site.value.themeConfig.sidebar, path.value)
const config = getSideBarConfig(theme.value.sidebar, path.value)
return isArray(config) ? getFlatSideBarLinks(config) : []
})
@ -25,7 +24,7 @@ export function useNextAndPrevLinks() {
const next = computed(() => {
if (
site.value.themeConfig.nextLinks !== false &&
theme.value.nextLinks !== false &&
index.value > -1 &&
index.value < candidates.value.length - 1
) {
@ -34,7 +33,7 @@ export function useNextAndPrevLinks() {
})
const prev = computed(() => {
if (site.value.themeConfig.prevLinks !== false && index.value > 0) {
if (theme.value.prevLinks !== false && index.value > 0) {
return candidates.value[index.value - 1]
}
})

@ -1,13 +1,14 @@
import { computed } from 'vue'
import { useSiteDataByRoute } from 'vitepress'
import { useData } from 'vitepress'
import type { DefaultTheme } from '../config'
import { EXTERNAL_URL_RE } from '../../shared'
export const platforms = ['GitHub', 'GitLab', 'Bitbucket'].map((platform) => {
return [platform, new RegExp(platform, 'i')] as const
})
export function useRepo() {
const site = useSiteDataByRoute()
const { site } = useData()
return computed(() => {
const theme = site.value.themeConfig as DefaultTheme.Config
@ -26,7 +27,7 @@ export function useRepo() {
function getRepoUrl(repo: string): string {
// if the full url is not provided, default to GitHub repo
return /^https?:/.test(repo) ? repo : `https://github.com/${repo}`
return EXTERNAL_URL_RE.test(repo) ? repo : `https://github.com/${repo}`
}
function getRepoText(url: string, text?: string): string {

@ -1,13 +1,13 @@
import { computed } from 'vue'
import { useRoute, useSiteDataByRoute } from 'vitepress'
import { Header } from '/@types/shared'
import { useRoute, useData } from 'vitepress'
import { Header } from '../../shared'
import { useActiveSidebarLinks } from '../composables/activeSidebarLink'
import { getSideBarConfig } from '../support/sideBar'
import { DefaultTheme } from '../config'
export function useSideBar() {
const route = useRoute()
const site = useSiteDataByRoute()
const { site } = useData()
useActiveSidebarLinks()
@ -22,7 +22,7 @@ export function useSideBar() {
return []
}
// if it's `atuo`, render headers of the current page
// if it's `auto`, render headers of the current page
if (frontSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
}

@ -1,13 +0,0 @@
import { useSiteData, joinPath } from 'vitepress'
export function useUrl() {
const site = useSiteData()
function withBase(path: string): string {
return joinPath(site.value.base, path)
}
return {
withBase
}
}

@ -1,146 +1 @@
export namespace DefaultTheme {
export interface Config {
logo?: string
nav?: NavItem[] | false
sidebar?: SideBarConfig | MultiSideBarConfig
/**
* GitHub repository following the format <user>/<project>.
*
* @example `"vuejs/vue-next"`
*/
repo?: string
/**
* Customize the header label. Defaults to GitHub/Gitlab/Bitbucket
* depending on the provided repo.
*
* @example `"Contribute!"`
*/
repoLabel?: string
/**
* If your docs are in a different repository from your main project.
*
* @example `"vuejs/docs-next"`
*/
docsRepo?: string
/**
* If your docs are not at the root of the repo.
*
* @example `"docs"`
*/
docsDir?: string
/**
* If your docs are in a different branch. Defaults to `master`.
*
* @example `"next"`
*/
docsBranch?: string
/**
* Enable links to edit pages at the bottom of the page.
*/
editLinks?: boolean
/**
* Custom text for edit link. Defaults to "Edit this page".
*/
editLinkText?: string
/**
* Show last updated time at the bottom of the page. Defaults to `false`.
* If given a string, it will be displayed as a prefix (default value:
* "Last Updated").
*/
lastUpdated?: string | boolean
prevLinks?: boolean
nextLinks?: boolean
locales?: Record<string, LocaleConfig & Omit<Config, 'locales'>>
algolia?: AlgoliaSearchOptions
carbonAds?: {
carbon: string
custom?: string
placement: string
}
}
// navbar --------------------------------------------------------------------
export type NavItem = NavItemWithLink | NavItemWithChildren
export interface NavItemBase {
text: string
target?: string
rel?: string
ariaLabel?: string
activeMatch?: string
}
export interface NavItemWithLink extends NavItemBase {
link: string
}
export interface NavItemWithChildren extends NavItemBase {
items: NavItemWithLink[]
}
// sidebar -------------------------------------------------------------------
export type SideBarConfig = SideBarItem[] | 'auto' | false
export interface MultiSideBarConfig {
[path: string]: SideBarConfig
}
export type SideBarItem = SideBarLink | SideBarGroup
export interface SideBarLink {
text: string
link: string
}
export interface SideBarGroup {
text: string
link?: string
/**
* @default false
*/
collapsable?: boolean
children: SideBarItem[]
}
// algolia ------------------------------------------------------------------
// partially copied from @docsearch/react/dist/esm/DocSearch.d.ts
export interface AlgoliaSearchOptions {
appId?: string
apiKey: string
indexName: string
placeholder?: string
searchParameters?: any
disableUserPersonalization?: boolean
initialQuery?: string
}
// locales -------------------------------------------------------------------
export interface LocaleConfig {
/**
* Text for the language dropdown.
*/
selectText?: string
/**
* Label for this locale in the language dropdown.
*/
label?: string
}
}
export { DefaultTheme } from '../shared'

@ -8,6 +8,7 @@ import { Theme } from 'vitepress'
import Layout from './Layout.vue'
import NotFound from './NotFound.vue'
export { DefaultTheme } from './config'
const theme: Theme = {
Layout,
NotFound

@ -26,6 +26,7 @@ div[class*='language-'] {
li > div[class*='language-'] {
border-radius: 6px 0 0 6px;
margin: 1rem -1.5rem 1rem -1.25rem;
line-height: initial;
}
@media (min-width: 420px) {
@ -54,6 +55,7 @@ li > div[class*='language-'] {
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
background: transparent;
}
[class*='language-'] pre {
@ -61,7 +63,6 @@ li > div[class*='language-'] {
z-index: 1;
margin: 0;
padding: 1.25rem 1.5rem;
background: transparent;
overflow-x: auto;
}
@ -116,7 +117,7 @@ div[class*='language-'].line-numbers-mode {
/* Language marker */
[class*='language-']:before {
div[class*='language-']:before {
position: absolute;
top: 0.6em;
right: 1em;
@ -125,93 +126,97 @@ div[class*='language-'].line-numbers-mode {
color: #888;
}
[class~='language-html']:before,
[class~='language-markup']:before {
div[class~='language-html']:before,
div[class~='language-markup']:before {
content: 'html';
}
[class~='language-md']:before,
[class~='language-markdown']:before {
div[class~='language-md']:before,
div[class~='language-markdown']:before {
content: 'md';
}
[class~='language-css']:before {
div[class~='language-css']:before {
content: 'css';
}
[class~='language-sass']:before {
div[class~='language-sass']:before {
content: 'sass';
}
[class~='language-scss']:before {
div[class~='language-scss']:before {
content: 'scss';
}
[class~='language-less']:before {
div[class~='language-less']:before {
content: 'less';
}
[class~='language-stylus']:before {
div[class~='language-stylus']:before {
content: 'styl';
}
[class~='language-js']:before,
[class~='language-javascript']:before {
div[class~='language-js']:before,
div[class~='language-javascript']:before {
content: 'js';
}
[class~='language-ts']:before,
[class~='language-typescript']:before {
div[class~='language-ts']:before,
div[class~='language-typescript']:before {
content: 'ts';
}
[class~='language-json']:before {
div[class~='language-json']:before {
content: 'json';
}
[class~='language-rb']:before,
[class~='language-ruby']:before {
div[class~='language-rb']:before,
div[class~='language-ruby']:before {
content: 'rb';
}
[class~='language-py']:before,
[class~='language-python']:before {
div[class~='language-py']:before,
div[class~='language-python']:before {
content: 'py';
}
[class~='language-sh']:before,
[class~='language-bash']:before {
div[class~='language-sh']:before,
div[class~='language-bash']:before {
content: 'sh';
}
[class~='language-php']:before {
div[class~='language-php']:before {
content: 'php';
}
[class~='language-go']:before {
div[class~='language-go']:before {
content: 'go';
}
[class~='language-rust']:before {
div[class~='language-rust']:before {
content: 'rust';
}
[class~='language-java']:before {
div[class~='language-java']:before {
content: 'java';
}
[class~='language-c']:before {
div[class~='language-c']:before {
content: 'c';
}
[class~='language-yaml']:before {
div[class~='language-yaml']:before {
content: 'yaml';
}
[class~='language-dockerfile']:before {
div[class~='language-dockerfile']:before {
content: 'dockerfile';
}
div[class~='language-vue']:before {
content: 'vue';
}
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML.
* Based on https://github.com/chriskempson/tomorrow-theme

@ -1,4 +1,5 @@
.custom-block.tip,
.custom-block.info,
.custom-block.warning,
.custom-block.danger {
margin: 1rem 0;
@ -12,6 +13,11 @@
border-color: var(--c-brand);
}
.custom-block.info {
background-color: #f3f5f7;
border-color: var(--c-text-light-2);
}
.custom-block.warning {
border-color: #e7c000;
color: #6b5900;

@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.json",
"extends": "../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "../../dist/client",
@ -8,11 +8,9 @@
"lib": ["ESNext", "DOM"],
"types": ["vite/client"],
"paths": {
"/@shared/*": ["shared/*"],
"/@types/*": ["../../types/*"],
"/@theme/*": ["theme-default/*"],
"vitepress": ["index.ts"]
}
},
"include": [".", "../../types/shared.d.ts"]
"include": ["."]
}

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

Loading…
Cancel
Save