Merge branch 'vuejs:main' into main

pull/3640/head
Timothy Lau 1 year ago committed by GitHub
commit 6bcf94604d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,12 +2,13 @@ name: Lock Threads
on: on:
schedule: schedule:
- cron: 0 0 * * * - cron: 38 4 * * *
workflow_dispatch: workflow_dispatch:
permissions: permissions:
issues: write issues: write
pull-requests: write pull-requests: write
discussions: write
concurrency: concurrency:
group: lock group: lock

@ -1,3 +1,15 @@
## [1.1.4](https://github.com/vuejs/vitepress/compare/v1.1.3...v1.1.4) (2024-04-27)
### Bug Fixes
- **dev:** multiple server instances being created when editing config too quickly ([#3835](https://github.com/vuejs/vitepress/issues/3835)) ([729a890](https://github.com/vuejs/vitepress/commit/729a890669c363895cfac39ece046926cad36d01))
- **theme/a11y:** add unique name to footer prev / next navigation landmark ([e60c101](https://github.com/vuejs/vitepress/commit/e60c101e50fa56d4cd54d434c5628cc7e2231318))
- **theme/a11y:** remove duplicate assistive text from outline nav ([#3803](https://github.com/vuejs/vitepress/issues/3803)) ([733d986](https://github.com/vuejs/vitepress/commit/733d986a84f614484b04235546dc4cda0769e833))
- **theme/i18n:** 404 page not showing localized text ([#3833](https://github.com/vuejs/vitepress/issues/3833)) ([cc11b8e](https://github.com/vuejs/vitepress/commit/cc11b8e41ec481320b03902bdc307d479a8ba838))
- **theme:** disable keypress effect on search button ([ccc37bb](https://github.com/vuejs/vitepress/commit/ccc37bb80e4147b9ab91b0f5d7dfae9d51533460))
- **theme:** don't use Chinese quotes on non-Chinese documents ([#3834](https://github.com/vuejs/vitepress/issues/3834)) ([75115f4](https://github.com/vuejs/vitepress/commit/75115f4f8223d67dab2dc82fadaf2941aabf6330))
- **theme:** leaking event listener when going back/forward on Safari on iOS ([#3658](https://github.com/vuejs/vitepress/issues/3658)) ([#3671](https://github.com/vuejs/vitepress/issues/3671)) ([1a72181](https://github.com/vuejs/vitepress/commit/1a72181c06d78e5e6f293e3f6abdb15caa4d2f53))
## [1.1.3](https://github.com/vuejs/vitepress/compare/v1.1.1...v1.1.3) (2024-04-18) ## [1.1.3](https://github.com/vuejs/vitepress/compare/v1.1.1...v1.1.3) (2024-04-18)
### Bug Fixes ### Bug Fixes

@ -3,13 +3,11 @@
VitePress comes with out-of-the-box support for generating a `sitemap.xml` file for your site. To enable it, add the following to your `.vitepress/config.js`: VitePress comes with out-of-the-box support for generating a `sitemap.xml` file for your site. To enable it, add the following to your `.vitepress/config.js`:
```ts ```ts
import { defineConfig } from 'vitepress' export default {
export default defineConfig({
sitemap: { sitemap: {
hostname: 'https://example.com' hostname: 'https://example.com'
} }
}) }
``` ```
To have `<lastmod>` tags in your `sitemap.xml`, you can enable the [`lastUpdated`](../reference/default-theme-last-updated) option. To have `<lastmod>` tags in your `sitemap.xml`, you can enable the [`lastUpdated`](../reference/default-theme-last-updated) option.
@ -19,14 +17,23 @@ To have `<lastmod>` tags in your `sitemap.xml`, you can enable the [`lastUpdated
Sitemap support is powered by the [`sitemap`](https://www.npmjs.com/package/sitemap) module. You can pass any options supported by it to the `sitemap` option in your config file. These will be passed directly to the `SitemapStream` constructor. Refer to the [`sitemap` documentation](https://www.npmjs.com/package/sitemap#options-you-can-pass) for more details. Example: Sitemap support is powered by the [`sitemap`](https://www.npmjs.com/package/sitemap) module. You can pass any options supported by it to the `sitemap` option in your config file. These will be passed directly to the `SitemapStream` constructor. Refer to the [`sitemap` documentation](https://www.npmjs.com/package/sitemap#options-you-can-pass) for more details. Example:
```ts ```ts
import { defineConfig } from 'vitepress' export default {
export default defineConfig({
sitemap: { sitemap: {
hostname: 'https://example.com', hostname: 'https://example.com',
lastmodDateOnly: false lastmodDateOnly: false
} }
}) }
```
If you're using `base` in your config, you should append it to the `hostname` option:
```ts
export default {
base: '/my-site/',
sitemap: {
hostname: 'https://example.com/my-site/'
}
}
``` ```
## `transformItems` Hook ## `transformItems` Hook
@ -34,9 +41,7 @@ export default defineConfig({
You can use the `sitemap.transformItems` hook to modify the sitemap items before they are written to the `sitemap.xml` file. This hook is called with an array of sitemap items and expects an array of sitemap items to be returned. Example: You can use the `sitemap.transformItems` hook to modify the sitemap items before they are written to the `sitemap.xml` file. This hook is called with an array of sitemap items and expects an array of sitemap items to be returned. Example:
```ts ```ts
import { defineConfig } from 'vitepress' export default {
export default defineConfig({
sitemap: { sitemap: {
hostname: 'https://example.com', hostname: 'https://example.com',
transformItems: (items) => { transformItems: (items) => {
@ -49,5 +54,5 @@ export default defineConfig({
return items return items
} }
} }
}) }
``` ```

@ -1,6 +1,6 @@
{ {
"name": "vitepress", "name": "vitepress",
"version": "1.1.3", "version": "1.1.4",
"description": "Vite & Vue powered static site generator", "description": "Vite & Vue powered static site generator",
"keywords": [ "keywords": [
"vite", "vite",
@ -104,19 +104,20 @@
"dependencies": { "dependencies": {
"@docsearch/css": "^3.6.0", "@docsearch/css": "^3.6.0",
"@docsearch/js": "^3.6.0", "@docsearch/js": "^3.6.0",
"@shikijs/core": "^1.3.0", "@shikijs/core": "^1.4.0",
"@shikijs/transformers": "^1.3.0", "@shikijs/transformers": "^1.4.0",
"@types/markdown-it": "^14.0.1", "@types/markdown-it": "^14.1.1",
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vue/devtools-api": "^7.0.27", "@vue/devtools-api": "^7.0.27",
"@vue/shared": "^3.4.26",
"@vueuse/core": "^10.9.0", "@vueuse/core": "^10.9.0",
"@vueuse/integrations": "^10.9.0", "@vueuse/integrations": "^10.9.0",
"focus-trap": "^7.5.4", "focus-trap": "^7.5.4",
"mark.js": "8.11.1", "mark.js": "8.11.1",
"minisearch": "^6.3.0", "minisearch": "^6.3.0",
"shiki": "^1.3.0", "shiki": "^1.4.0",
"vite": "^5.2.9", "vite": "^5.2.11",
"vue": "^3.4.23" "vue": "^3.4.26"
}, },
"devDependencies": { "devDependencies": {
"@clack/prompts": "^0.7.0", "@clack/prompts": "^0.7.0",
@ -141,13 +142,12 @@
"@types/mark.js": "^8.11.12", "@types/mark.js": "^8.11.12",
"@types/markdown-it-attrs": "^4.1.3", "@types/markdown-it-attrs": "^4.1.3",
"@types/markdown-it-container": "^2.0.10", "@types/markdown-it-container": "^2.0.10",
"@types/markdown-it-emoji": "^2.0.5", "@types/markdown-it-emoji": "^3.0.1",
"@types/micromatch": "^4.0.7", "@types/micromatch": "^4.0.7",
"@types/minimist": "^1.2.5", "@types/minimist": "^1.2.5",
"@types/node": "^20.12.7", "@types/node": "^20.12.8",
"@types/postcss-prefix-selector": "^1.16.3", "@types/postcss-prefix-selector": "^1.16.3",
"@types/prompts": "^2.4.9", "@types/prompts": "^2.4.9",
"@vue/shared": "^3.4.23",
"chokidar": "^3.6.0", "chokidar": "^3.6.0",
"conventional-changelog-cli": "^4.1.0", "conventional-changelog-cli": "^4.1.0",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
@ -161,7 +161,7 @@
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"lint-staged": "^15.2.2", "lint-staged": "^15.2.2",
"lodash.template": "^4.5.0", "lodash.template": "^4.5.0",
"lru-cache": "^10.2.0", "lru-cache": "^10.2.2",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-anchor": "^8.6.7", "markdown-it-anchor": "^8.6.7",
"markdown-it-attrs": "^4.1.6", "markdown-it-attrs": "^4.1.6",
@ -179,12 +179,12 @@
"pkg-dir": "^8.0.0", "pkg-dir": "^8.0.0",
"playwright-chromium": "^1.43.1", "playwright-chromium": "^1.43.1",
"polka": "1.0.0-next.25", "polka": "1.0.0-next.25",
"postcss-prefix-selector": "^1.16.0", "postcss-prefix-selector": "^1.16.1",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"prompts": "^2.4.2", "prompts": "^2.4.2",
"punycode": "^2.3.1", "punycode": "^2.3.1",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^4.14.3", "rollup": "^4.17.2",
"rollup-plugin-dts": "^6.1.0", "rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.1", "rollup-plugin-esbuild": "^6.1.1",
"semver": "^7.6.0", "semver": "^7.6.0",
@ -194,8 +194,8 @@
"sort-package-json": "^2.10.0", "sort-package-json": "^2.10.0",
"supports-color": "^9.4.0", "supports-color": "^9.4.0",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"vitest": "^1.5.0", "vitest": "^1.5.3",
"vue-tsc": "^2.0.13", "vue-tsc": "2.0.14",
"wait-on": "^7.2.0" "wait-on": "^7.2.0"
}, },
"peerDependencies": { "peerDependencies": {
@ -210,7 +210,7 @@
"optional": true "optional": true
} }
}, },
"packageManager": "pnpm@9.0.2", "packageManager": "pnpm@9.0.6",
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {
"ignoreMissing": [ "ignoreMissing": [
@ -223,8 +223,8 @@
"ora>string-width": "^5" "ora>string-width": "^5"
}, },
"patchedDependencies": { "patchedDependencies": {
"@types/markdown-it@14.0.1": "patches/@types__markdown-it@14.0.1.patch", "markdown-it-anchor@8.6.7": "patches/markdown-it-anchor@8.6.7.patch",
"markdown-it-anchor@8.6.7": "patches/markdown-it-anchor@8.6.7.patch" "rollup-plugin-dts@6.1.0": "patches/rollup-plugin-dts@6.1.0.patch"
} }
} }
} }

@ -1,13 +0,0 @@
diff --git a/package.json b/package.json
index 3b3cdfc4427a1a64fdd3b37604a7174e4646e423..afaea16b115554fcf15a905642562e881ece7ca6 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
}
],
"main": "",
- "types": "index.d.ts",
+ "types": "index.d.mts",
"exports": {
".": {
"import": "./index.d.mts",

@ -0,0 +1,13 @@
diff --git a/dist/rollup-plugin-dts.mjs b/dist/rollup-plugin-dts.mjs
index 4a9412285c48c37d03340a086c771f8e61fd82ac..c73cba3bf47550f69011366e37d2ae974f0c9fc0 100644
--- a/dist/rollup-plugin-dts.mjs
+++ b/dist/rollup-plugin-dts.mjs
@@ -675,6 +675,8 @@ function preProcess({ sourceFile }) {
const nextToken = children[idx + 1];
const isPunctuation = nextToken.kind >= ts.SyntaxKind.FirstPunctuation && nextToken.kind <= ts.SyntaxKind.LastPunctuation;
if (isPunctuation) {
+ const addSpace = code.slice(token.getEnd(), nextToken.getStart()) != " ";
+ code.appendLeft(nextToken.getStart(), `${addSpace ? " " : ""}${defaultExport}`);
code.appendLeft(nextToken.getStart(), defaultExport);
}
else {

File diff suppressed because it is too large Load Diff

@ -11,15 +11,15 @@ import replace from '@rollup/plugin-replace'
import alias from '@rollup/plugin-alias' import alias from '@rollup/plugin-alias'
import dts from 'rollup-plugin-dts' import dts from 'rollup-plugin-dts'
const ROOT = fileURLToPath(import.meta.url)
const r = (p: string) => resolve(ROOT, '..', p)
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const pkg = require('./package.json') const pkg = require(r('package.json'))
const DEV = !!process.env.DEV const DEV = !!process.env.DEV
const PROD = !DEV const PROD = !DEV
const ROOT = fileURLToPath(import.meta.url)
const r = (p: string) => resolve(ROOT, '..', p)
const external = [ const external = [
...Object.keys(pkg.dependencies), ...Object.keys(pkg.dependencies),
...Object.keys(pkg.peerDependencies), ...Object.keys(pkg.peerDependencies),
@ -29,11 +29,7 @@ const external = [
] ]
const plugins = [ const plugins = [
alias({ alias({ entries: { 'readable-stream': 'stream' } }),
entries: {
'readable-stream': 'stream'
}
}),
replace({ replace({
// polyfill broken browser check from bundled deps // polyfill broken browser check from bundled deps
'navigator.userAgentData': 'undefined', 'navigator.userAgentData': 'undefined',
@ -69,21 +65,34 @@ const typesExternal = [
'fast-glob' 'fast-glob'
] ]
const dtsNode = dts({
respectExternal: true,
tsconfig: r('src/node/tsconfig.json')
})
const originalResolveId = dtsNode.resolveId
dtsNode.resolveId = async function (source, importer) {
const res = await (originalResolveId as Function).call(this, source, importer)
if (res?.id) res.id = await fs.realpath(res.id)
return res
}
const nodeTypes: RollupOptions = { const nodeTypes: RollupOptions = {
input: r('src/node/index.ts'), input: r('src/node/index.ts'),
output: { output: {
format: 'esm', format: 'esm',
file: 'dist/node/index.d.ts' file: r('dist/node/index.d.ts')
}, },
external: typesExternal, external: typesExternal,
plugins: [dts({ respectExternal: true })] plugins: [dtsNode]
} }
const clientTypes: RollupOptions = { const clientTypes: RollupOptions = {
input: r('dist/client-types/index.d.ts'), input: r('dist/client-types/index.d.ts'),
output: { output: {
format: 'esm', format: 'esm',
file: 'dist/client/index.d.ts' file: r('dist/client/index.d.ts')
}, },
external: typesExternal, external: typesExternal,
plugins: [ plugins: [
@ -99,10 +108,4 @@ const clientTypes: RollupOptions = {
] ]
} }
const config = defineConfig([]) export default defineConfig([esmBuild, nodeTypes, clientTypes])
config.push(esmBuild)
config.push(nodeTypes)
config.push(clientTypes)
export default config

@ -137,8 +137,21 @@ function newRouter(): Router {
pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js') pageFilePath = pageFilePath.replace(/\.js$/, '.lean.js')
} }
if (import.meta.env.SSR) { if (import.meta.env.DEV) {
pageModule = import(/*@vite-ignore*/ pageFilePath + '?t=' + Date.now()) pageModule = import(/*@vite-ignore*/ pageFilePath).catch(() => {
// try with/without trailing slash
// in prod this is handled in src/client/app/utils.ts#pathToFile
const url = new URL(pageFilePath!, 'http://a.com')
const path =
(url.pathname.endsWith('/index.md')
? url.pathname.slice(0, -9) + '.md'
: url.pathname.slice(0, -3) + '/index.md') +
url.search +
url.hash
return import(/*@vite-ignore*/ path)
})
} else if (import.meta.env.SSR) {
pageModule = import(/*@vite-ignore*/ `${pageFilePath}?t=${Date.now()}`)
} else { } else {
pageModule = import(/*@vite-ignore*/ pageFilePath) pageModule = import(/*@vite-ignore*/ pageFilePath)
} }

@ -156,7 +156,13 @@ export function createRouter(
latestPendingPath = null latestPendingPath = null
route.path = inBrowser ? pendingPath : withBase(pendingPath) route.path = inBrowser ? pendingPath : withBase(pendingPath)
route.component = fallbackComponent ? markRaw(fallbackComponent) : null route.component = fallbackComponent ? markRaw(fallbackComponent) : null
route.data = notFoundPageData const relativePath = inBrowser
? pendingPath
.replace(/(^|\/)$/, '$1index')
.replace(/(\.html)?$/, '.md')
.replace(/^\//, '')
: '404.md'
route.data = { ...notFoundPageData, relativePath }
} }
} }
} }

@ -1,55 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { withBase } from 'vitepress' import { withBase } from 'vitepress'
import { useData } from './composables/data' import { useData } from './composables/data'
import { useLangs } from './composables/langs' import { useLangs } from './composables/langs'
const { site } = useData() const { theme } = useData()
const { localeLinks } = useLangs({ removeCurrent: false }) const { currentLang } = useLangs()
const locale = ref({
link: '/',
index: 'root'
})
onMounted(() => {
const path = window.location.pathname
.replace(site.value.base, '')
.replace(/(^.*?\/).*$/, '/$1')
if (localeLinks.value.length) {
locale.value =
localeLinks.value.find(({ link }) => link.startsWith(path)) ||
localeLinks.value[0]
}
})
const notFound = computed(() => ({
code: 404,
title: 'PAGE NOT FOUND',
quote:
"But if you don't change your direction, and if you keep looking, you may end up where you are heading.",
linkLabel: 'go to home',
linkText: 'Take me home',
...(locale.value.index === 'root'
? site.value.themeConfig?.notFound
: site.value.locales?.[locale.value.index]?.themeConfig?.notFound)
}))
</script> </script>
<template> <template>
<div class="NotFound"> <div class="NotFound">
<p class="code">{{ notFound.code }}</p> <p class="code">{{ theme.notFound?.code ?? '404' }}</p>
<h1 class="title">{{ notFound.title }}</h1> <h1 class="title">{{ theme.notFound?.title ?? 'PAGE NOT FOUND' }}</h1>
<div class="divider" /> <div class="divider" />
<blockquote class="quote">{{ notFound.quote }}</blockquote> <blockquote class="quote">
{{
theme.notFound?.quote ??
"But if you don't change your direction, and if you keep looking, you may end up where you are heading."
}}
</blockquote>
<div class="action"> <div class="action">
<a <a
class="link" class="link"
:href="withBase(locale.link)" :href="withBase(currentLang.link)"
:aria-label="notFound.linkLabel" :aria-label="theme.notFound?.linkLabel ?? 'go to home'"
> >
{{ notFound.linkText }} {{ theme.notFound?.linkText ?? 'Take me home' }}
</a> </a>
</div> </div>
</div> </div>

@ -1,7 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useWindowSize } from '@vueuse/core' import { useWindowSize } from '@vueuse/core'
const { width: vw } = useWindowSize({ includeScrollbar: false }) const { width: vw } = useWindowSize({
initialWidth: 0,
includeScrollbar: false
})
</script> </script>
<template> <template>
@ -46,7 +49,7 @@ const { width: vw } = useWindowSize({ includeScrollbar: false })
} }
.vp-doc :deep(.VPHomeSponsors a), .vp-doc :deep(.VPHomeSponsors a),
.vp-doc :deep(.VPTeamPage a){ .vp-doc :deep(.VPTeamPage a) {
text-decoration: none; text-decoration: none;
} }
</style> </style>

@ -12,7 +12,11 @@ const props = defineProps<{
}>() }>()
const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span')) const tag = computed(() => props.tag ?? (props.href ? 'a' : 'span'))
const isExternal = computed(() => props.href && EXTERNAL_URL_RE.test(props.href)) const isExternal = computed(
() =>
(props.href && EXTERNAL_URL_RE.test(props.href)) ||
props.target === '_blank'
)
</script> </script>
<template> <template>

@ -291,11 +291,7 @@ watch(results, (r) => {
function scrollToSelectedResult() { function scrollToSelectedResult() {
nextTick(() => { nextTick(() => {
const selectedEl = document.querySelector('.result.selected') const selectedEl = document.querySelector('.result.selected')
if (selectedEl) { selectedEl?.scrollIntoView({ block: 'nearest' })
selectedEl.scrollIntoView({
block: 'nearest'
})
}
}) })
} }

@ -2,13 +2,9 @@ import { computed } from 'vue'
import { ensureStartingSlash } from '../support/utils' import { ensureStartingSlash } from '../support/utils'
import { useData } from './data' import { useData } from './data'
export function useLangs({ export function useLangs({ correspondingLink = false } = {}) {
removeCurrent = true,
correspondingLink = false
} = {}) {
const { site, localeIndex, page, theme, hash } = useData() const { site, localeIndex, page, theme, hash } = useData()
const currentLang = computed(() => ({ const currentLang = computed(() => ({
index: localeIndex.value,
label: site.value.locales[localeIndex.value]?.label, label: site.value.locales[localeIndex.value]?.label,
link: link:
site.value.locales[localeIndex.value]?.link || site.value.locales[localeIndex.value]?.link ||
@ -17,10 +13,9 @@ export function useLangs({
const localeLinks = computed(() => const localeLinks = computed(() =>
Object.entries(site.value.locales).flatMap(([key, value]) => Object.entries(site.value.locales).flatMap(([key, value]) =>
removeCurrent && currentLang.value.label === value.label currentLang.value.label === value.label
? [] ? []
: { : {
index: key,
text: value.label, text: value.label,
link: link:
normalizeLink( normalizeLink(

@ -146,12 +146,40 @@ html body {
U+2193, U+2212, U+2215, U+FEFF, U+FFFD; U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
} }
/* Chinese quotes rendering fix. 中英文弯引号共享 Unicode 码位,确保引号使用中文字体渲染 */
@font-face { @font-face {
font-family: 'Chinese Quotes'; font-family: 'Punctuation SC';
src: local('PingFang SC Regular'), local('PingFang SC'), local('SimHei'), font-weight: 400;
local('Source Han Sans SC'); src: local('PingFang SC Regular'), local('Noto Sans CJK SC'),
unicode-range: U+2018, U+2019, U+201C, U+201D; /* 分别是 ‘’“” */ local('Microsoft YaHei');
unicode-range: U+201C, U+201D, U+2018, U+2019, U+2E3A, U+2014, U+2013, U+2026,
U+00B7, U+007E, U+002F;
}
@font-face {
font-family: 'Punctuation SC';
font-weight: 500;
src: local('PingFang SC Medium'), local('Noto Sans CJK SC'),
local('Microsoft YaHei');
unicode-range: U+201C, U+201D, U+2018, U+2019, U+2E3A, U+2014, U+2013, U+2026,
U+00B7, U+007E, U+002F;
}
@font-face {
font-family: 'Punctuation SC';
font-weight: 600;
src: local('PingFang SC Semibold'), local('Noto Sans CJK SC Bold'),
local('Microsoft YaHei Bold');
unicode-range: U+201C, U+201D, U+2018, U+2019, U+2E3A, U+2014, U+2013, U+2026,
U+00B7, U+007E, U+002F;
}
@font-face {
font-family: 'Punctuation SC';
font-weight: 700;
src: local('PingFang SC Semibold'), local('Noto Sans CJK SC Bold'),
local('Microsoft YaHei Bold');
unicode-range: U+201C, U+201D, U+2018, U+2019, U+2E3A, U+2014, U+2013, U+2026,
U+00B7, U+007E, U+002F;
} }
/* Generate the subsetted fonts using: `pyftsubset <file>.woff2 --unicodes="<range>" --output-file="inter-<style>-<subset>.woff2" --flavor=woff2` */ /* Generate the subsetted fonts using: `pyftsubset <file>.woff2 --unicodes="<range>" --output-file="inter-<style>-<subset>.woff2" --flavor=woff2` */

@ -261,14 +261,20 @@
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
:root { :root {
--vp-font-family-base: 'Chinese Quotes', Inter, ui-sans-serif, system-ui, --vp-font-family-base: 'Inter', ui-sans-serif, system-ui, sans-serif,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
'Noto Color Emoji'; --vp-font-family-mono: ui-monospace, 'Menlo', 'Monaco', 'Consolas',
--vp-font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace; 'Liberation Mono', 'Courier New', monospace;
font-optical-sizing: auto; font-optical-sizing: auto;
} }
:root:where(:lang(zh)) {
--vp-font-family-base: 'Punctuation SC', 'Inter', ui-sans-serif, system-ui,
'PingFang SC', 'Noto Sans CJK SC', 'Noto Sans SC', 'Heiti SC', 'DengXian',
'Microsoft YaHei', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
}
/** /**
* Shadows * Shadows
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

@ -3,11 +3,8 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"outDir": "../../dist/client", "outDir": "../../dist/client",
"moduleResolution": "bundler",
"declaration": true, "declaration": true,
"declarationDir": "../../dist/client-types", "declarationDir": "../../dist/client-types",
"jsx": "preserve",
"lib": ["esnext", "dom", "dom.iterable"],
"types": ["vite/client"], "types": ["vite/client"],
"paths": { "paths": {
"vitepress": ["index.ts"], "vitepress": ["index.ts"],

@ -180,7 +180,7 @@ export async function renderPage(
${await renderHead(head)} ${await renderHead(head)}
</head> </head>
<body>${teleports?.body || ''} <body>${teleports?.body || ''}
<div id="app">${content}</div> <div id="app">${page === '404.md' ? '' : content}</div>
${metadataScript.inHead ? '' : metadataScript.html} ${metadataScript.inHead ? '' : metadataScript.html}
${inlinedScript} ${inlinedScript}
</body> </body>

@ -20,6 +20,8 @@ if (root) {
argv.root = root argv.root = root
} }
let restartPromise: Promise<void> | undefined
if (!command || command === 'dev') { if (!command || command === 'dev') {
if (argv.force) { if (argv.force) {
delete argv.force delete argv.force
@ -28,8 +30,16 @@ if (!command || command === 'dev') {
const createDevServer = async () => { const createDevServer = async () => {
const server = await createServer(root, argv, async () => { const server = await createServer(root, argv, async () => {
await server.close() if (!restartPromise) {
await createDevServer() restartPromise = (async () => {
await server.close()
await createDevServer()
})().finally(() => {
restartPromise = undefined
})
}
return restartPromise
}) })
await server.listen() await server.listen()
logVersion(server.config.logger) logVersion(server.config.logger)
@ -38,7 +48,7 @@ if (!command || command === 'dev') {
} }
createDevServer().catch((err) => { createDevServer().catch((err) => {
createLogger().error( createLogger().error(
`${c.red(`failed to start server. error:`)}\n${err.stack}` `${c.red(`failed to start server. error:`)}\n${err.message}\n${err.stack}`
) )
process.exit(1) process.exit(1)
}) })
@ -49,13 +59,15 @@ if (!command || command === 'dev') {
logVersion() logVersion()
if (command === 'build') { if (command === 'build') {
build(root, argv).catch((err) => { build(root, argv).catch((err) => {
createLogger().error(`${c.red(`build error:`)}\n${err.stack}`) createLogger().error(
`${c.red(`build error:`)}\n${err.message}\n${err.stack}`
)
process.exit(1) process.exit(1)
}) })
} else if (command === 'serve' || command === 'preview') { } else if (command === 'serve' || command === 'preview') {
serve(argv).catch((err) => { serve(argv).catch((err) => {
createLogger().error( createLogger().error(
`${c.red(`failed to start server. error:`)}\n${err.stack}` `${c.red(`failed to start server. error:`)}\n${err.message}\n${err.stack}`
) )
process.exit(1) process.exit(1)
}) })

@ -18,7 +18,6 @@ import type { Options } from 'markdown-it'
import MarkdownIt from 'markdown-it' import MarkdownIt from 'markdown-it'
import anchorPlugin from 'markdown-it-anchor' import anchorPlugin from 'markdown-it-anchor'
import attrsPlugin from 'markdown-it-attrs' import attrsPlugin from 'markdown-it-attrs'
// @ts-expect-error: types of markdown-it-emoji are not up-to-date
import { full as emojiPlugin } from 'markdown-it-emoji' import { full as emojiPlugin } from 'markdown-it-emoji'
import type { import type {
BuiltinTheme, BuiltinTheme,
@ -112,6 +111,11 @@ export interface MarkdownOptions extends Options {
* Setup Shiki instance * Setup Shiki instance
*/ */
shikiSetup?: (shiki: Highlighter) => void | Promise<void> shikiSetup?: (shiki: Highlighter) => void | Promise<void>
/**
* The tooltip text for the copy button in code blocks
* @default 'Copy Code'
*/
codeCopyButtonTitle?: string
/* ==================== Markdown It Plugins ==================== */ /* ==================== Markdown It Plugins ==================== */
@ -196,6 +200,7 @@ export const createMarkdownRenderer = async (
logger: Pick<Logger, 'warn'> = console logger: Pick<Logger, 'warn'> = console
): Promise<MarkdownRenderer> => { ): Promise<MarkdownRenderer> => {
const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' } const theme = options.theme ?? { light: 'github-light', dark: 'github-dark' }
const codeCopyButtonTitle = options.codeCopyButtonTitle || 'Copy Code'
const hasSingleTheme = typeof theme === 'string' || 'name' in theme const hasSingleTheme = typeof theme === 'string' || 'name' in theme
const md = MarkdownIt({ const md = MarkdownIt({
@ -215,7 +220,7 @@ export const createMarkdownRenderer = async (
// custom plugins // custom plugins
md.use(componentPlugin, { ...options.component }) md.use(componentPlugin, { ...options.component })
.use(highlightLinePlugin) .use(highlightLinePlugin)
.use(preWrapperPlugin, { hasSingleTheme }) .use(preWrapperPlugin, { codeCopyButtonTitle, hasSingleTheme })
.use(snippetPlugin, srcDir) .use(snippetPlugin, srcDir)
.use(containerPlugin, { hasSingleTheme }, options.container) .use(containerPlugin, { hasSingleTheme }, options.container)
.use(imagePlugin, options.image) .use(imagePlugin, options.image)
@ -230,7 +235,7 @@ export const createMarkdownRenderer = async (
md.use(gitHubAlertsPlugin) md.use(gitHubAlertsPlugin)
} }
// 3rd party plugins // third party plugins
if (!options.attrs?.disable) { if (!options.attrs?.disable) {
md.use(attrsPlugin, options.attrs) md.use(attrsPlugin, options.attrs)
} }

@ -1,8 +1,10 @@
import type MarkdownIt from 'markdown-it' import type MarkdownIt from 'markdown-it'
import container from 'markdown-it-container'
import type { RenderRule } from 'markdown-it/lib/renderer.mjs' import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
import type Token from 'markdown-it/lib/token.mjs' import type Token from 'markdown-it/lib/token.mjs'
import container from 'markdown-it-container'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import type { MarkdownEnv } from '../../shared'
import { import {
extractTitle, extractTitle,
getAdaptiveThemeMarker, getAdaptiveThemeMarker,
@ -60,7 +62,7 @@ function createContainer(
container, container,
klass, klass,
{ {
render(tokens, idx, _options, env) { render(tokens, idx, _options, env: MarkdownEnv & { references?: any }) {
const token = tokens[idx] const token = tokens[idx]
const info = token.info.trim().slice(klass.length).trim() const info = token.info.trim().slice(klass.length).trim()
const attrs = md.renderer.renderAttrs(token) const attrs = md.renderer.renderAttrs(token)
@ -86,7 +88,7 @@ function createCodeGroup(options: Options): ContainerArgs {
if (tokens[idx].nesting === 1) { if (tokens[idx].nesting === 1) {
const name = nanoid(5) const name = nanoid(5)
let tabs = '' let tabs = ''
let checked = 'checked="checked"' let checked = 'checked'
for ( for (
let i = idx + 1; let i = idx + 1;

@ -1,6 +1,7 @@
import type MarkdownIt from 'markdown-it' import type MarkdownIt from 'markdown-it'
export interface Options { export interface Options {
codeCopyButtonTitle: string
hasSingleTheme: boolean hasSingleTheme: boolean
} }
@ -17,10 +18,14 @@ export function preWrapperPlugin(md: MarkdownIt, options: Options) {
token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ') token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ')
const lang = extractLang(token.info) const lang = extractLang(token.info)
const rawCode = fence(...args)
return `<div class="language-${lang}${getAdaptiveThemeMarker( return (
options `<div class="language-${lang}${getAdaptiveThemeMarker(options)}${active}">` +
)}${active}"><button title="Copy Code" class="copy"></button><span class="lang">${lang}</span>${rawCode}</div>` `<button title="${options.codeCopyButtonTitle}" class="copy"></button>` +
`<span class="lang">${lang}</span>` +
fence(...args) +
'</div>'
)
} }
} }

@ -11,11 +11,12 @@ import {
} from './markdown/markdown' } from './markdown/markdown'
import { import {
EXTERNAL_URL_RE, EXTERNAL_URL_RE,
getLocaleForPath,
slash, slash,
treatAsHtml,
type HeadConfig, type HeadConfig,
type MarkdownEnv, type MarkdownEnv,
type PageData, type PageData
treatAsHtml
} from './shared' } from './shared'
import { getGitTimestamp } from './utils/getGitTimestamp' import { getGitTimestamp } from './utils/getGitTimestamp'
import { processIncludes } from './utils/processIncludes' import { processIncludes } from './utils/processIncludes'
@ -95,13 +96,16 @@ export async function createMarkdownToVueRenderFn(
let includes: string[] = [] let includes: string[] = []
src = processIncludes(srcDir, src, fileOrig, includes) src = processIncludes(srcDir, src, fileOrig, includes)
const localeIndex = getLocaleForPath(siteConfig?.site, relativePath)
// reset env before render // reset env before render
const env: MarkdownEnv = { const env: MarkdownEnv = {
path: file, path: file,
relativePath, relativePath,
cleanUrls, cleanUrls,
includes, includes,
realPath: fileOrig realPath: fileOrig,
localeIndex
} }
const html = md.render(src, env) const html = md.render(src, env)
const { const {

@ -200,8 +200,10 @@ export async function resolveDynamicRoutes(
'silent' 'silent'
)) as RouteModule )) as RouteModule
routeModuleCache.set(pathsFile, mod) routeModuleCache.set(pathsFile, mod)
} catch (e: any) { } catch (err: any) {
logger.warn(`${c.yellow(`Failed to load ${pathsFile}:`)}\n${e.stack}`) logger.warn(
`${c.yellow(`Failed to load ${pathsFile}:`)}\n${err.message}\n${err.stack}`
)
continue continue
} }
} }

@ -7,7 +7,7 @@ import type { Plugin, ViteDevServer } from 'vite'
import type { SiteConfig } from '../config' import type { SiteConfig } from '../config'
import { createMarkdownRenderer } from '../markdown/markdown' import { createMarkdownRenderer } from '../markdown/markdown'
import { import {
resolveSiteDataByRoute, getLocaleForPath,
slash, slash,
type DefaultTheme, type DefaultTheme,
type MarkdownEnv type MarkdownEnv
@ -83,12 +83,6 @@ export async function localSearchPlugin(
return index return index
} }
function getLocaleForPath(file: string) {
const relativePath = slash(path.relative(siteConfig.srcDir, file))
const siteData = resolveSiteDataByRoute(siteConfig.site, relativePath)
return siteData?.localeIndex ?? 'root'
}
let server: ViteDevServer | undefined let server: ViteDevServer | undefined
function onIndexUpdated() { function onIndexUpdated() {
@ -126,7 +120,7 @@ export async function localSearchPlugin(
const file = path.join(siteConfig.srcDir, page) const file = path.join(siteConfig.srcDir, page)
// get file metadata // get file metadata
const fileId = getDocId(file) const fileId = getDocId(file)
const locale = getLocaleForPath(file) const locale = getLocaleForPath(siteConfig.site, page)
const index = getIndexByLocale(locale) const index = getIndexByLocale(locale)
// retrieve file and split into "sections" // retrieve file and split into "sections"
const html = await render(file) const html = await render(file)

@ -1,10 +1,8 @@
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "es2022",
"baseUrl": ".", "baseUrl": ".",
"outDir": "../../dist/node", "outDir": "../../dist/node",
"module": "esnext",
"types": ["node"], "types": ["node"],
"sourceMap": true "sourceMap": true
}, },

@ -24,7 +24,7 @@ const INDEX_OR_EXT_RE = /(?:(^|\/)index)?\.(?:md|html)$/
export const inBrowser = typeof document !== 'undefined' export const inBrowser = typeof document !== 'undefined'
export const notFoundPageData: PageData = { export const notFoundPageData: PageData = {
relativePath: '', relativePath: '404.md',
filePath: '', filePath: '',
title: '404', title: '404',
description: 'Not Found', description: 'Not Found',
@ -72,6 +72,20 @@ export function isExternal(path: string): boolean {
return EXTERNAL_URL_RE.test(path) return EXTERNAL_URL_RE.test(path)
} }
export function getLocaleForPath(
siteData: SiteData | undefined,
relativePath: string
): string {
return (
Object.keys(siteData?.locales || {}).find(
(key) =>
key !== 'root' &&
!isExternal(key) &&
isActive(relativePath, `/${key}/`, true)
) || 'root'
)
}
/** /**
* this merges the locales data to the main data by the route * this merges the locales data to the main data by the route
*/ */
@ -79,13 +93,7 @@ export function resolveSiteDataByRoute(
siteData: SiteData, siteData: SiteData,
relativePath: string relativePath: string
): SiteData { ): SiteData {
const localeIndex = const localeIndex = getLocaleForPath(siteData, relativePath)
Object.keys(siteData.locales).find(
(key) =>
key !== 'root' &&
!isExternal(key) &&
isActive(relativePath, `/${key}/`, true)
) || 'root'
return Object.assign({}, siteData, { return Object.assign({}, siteData, {
localeIndex, localeIndex,

@ -2,7 +2,7 @@
"compilerOptions": { "compilerOptions": {
"module": "esnext", "module": "esnext",
"target": "esnext", "target": "esnext",
"moduleResolution": "node", "moduleResolution": "bundler",
"esModuleInterop": true, "esModuleInterop": true,
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,

7
types/shared.d.ts vendored

@ -7,7 +7,11 @@ export type Awaitable<T> = T | PromiseLike<T>
export interface PageData { export interface PageData {
relativePath: string relativePath: string
filePath: string // differs from relativePath in case of path rewrites /**
* differs from relativePath in case of path rewrites
* empty string if the page is virtual (e.g. 404 page)
*/
filePath: string
title: string title: string
titleTemplate?: string | boolean titleTemplate?: string | boolean
description: string description: string
@ -196,4 +200,5 @@ export interface MarkdownEnv {
links?: string[] links?: string[]
includes?: string[] includes?: string[]
realPath?: string realPath?: string
localeIndex?: string
} }

Loading…
Cancel
Save