diff --git a/src/client/theme-default/composables/nav.ts b/src/client/theme-default/composables/nav.ts
index 4d24c01c..e3cf686b 100644
--- a/src/client/theme-default/composables/nav.ts
+++ b/src/client/theme-default/composables/nav.ts
@@ -1,5 +1,5 @@
import type { DefaultTheme } from 'vitepress/theme'
-import { ref, computed } from 'vue'
+import { ref, computed, watch } from 'vue'
import { useData, useRoute } from 'vitepress'
export function useNav() {
@@ -26,6 +26,9 @@ export function useNav() {
window.outerWidth >= 768 && closeScreen()
}
+ const route = useRoute()
+ watch(() => route.path, closeScreen)
+
return {
isScreenOpen,
openScreen,
diff --git a/src/client/theme-default/composables/prev-next.ts b/src/client/theme-default/composables/prev-next.ts
index e09cb0e0..0e97e695 100644
--- a/src/client/theme-default/composables/prev-next.ts
+++ b/src/client/theme-default/composables/prev-next.ts
@@ -4,7 +4,7 @@ import { isActive } from '../support/utils'
import { getSidebar, getFlatSideBarLinks } from '../support/sidebar'
export function usePrevNext() {
- const { page, theme } = useData()
+ const { page, theme, frontmatter } = useData()
return computed(() => {
const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath)
@@ -15,8 +15,12 @@ export function usePrevNext() {
})
return {
- prev: candidates[index - 1],
- next: candidates[index + 1]
+ prev: frontmatter.value.prev
+ ? { ...candidates[index - 1], text: frontmatter.value.prev }
+ : candidates[index - 1],
+ next: frontmatter.value.next
+ ? { ...candidates[index + 1], text: frontmatter.value.next }
+ : candidates[index + 1]
}
})
}
diff --git a/src/client/theme-default/composables/sidebar.ts b/src/client/theme-default/composables/sidebar.ts
index 40f725c8..7baedde4 100644
--- a/src/client/theme-default/composables/sidebar.ts
+++ b/src/client/theme-default/composables/sidebar.ts
@@ -1,5 +1,5 @@
-import { Ref, ref, computed, watchEffect, onMounted, onUnmounted } from 'vue'
-import { useRoute, useData } from 'vitepress'
+import { computed, onMounted, onUnmounted, Ref, ref, watchEffect } from 'vue'
+import { useData, useRoute } from 'vitepress'
import { getSidebar } from '../support/sidebar'
export function useSidebar() {
@@ -10,9 +10,7 @@ export function useSidebar() {
const sidebar = computed(() => {
const sidebarConfig = theme.value.sidebar
- const relativePath = route.data.relativePath
-
- return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : []
+ return sidebarConfig ? getSidebar(sidebarConfig, route.path) : []
})
const hasSidebar = computed(() => {
diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts
index d974f9b2..01dd1f2b 100644
--- a/src/node/markdownToVue.ts
+++ b/src/node/markdownToVue.ts
@@ -35,14 +35,7 @@ export async function createMarkdownToVueRenderFn(
pages = pages.map((p) => slash(p.replace(/\.md$/, '')))
- const userDefineRegex = userDefines
- ? new RegExp(
- `\\b(${Object.keys(userDefines)
- .map((key) => key.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'))
- .join('|')})`,
- 'g'
- )
- : null
+ const replaceRegex = genReplaceRegexp(userDefines, isBuild)
return async (
src: string,
@@ -75,24 +68,9 @@ export async function createMarkdownToVueRenderFn(
md.__path = file
md.__relativePath = relativePath
- let html = md.render(content)
+ const html = md.render(content)
const data = md.__data
- if (isBuild) {
- // avoid env variables being replaced by vite
- html = html
- .replace(/\bimport\.meta/g, 'import.
meta')
- .replace(/\bprocess\.env/g, 'process.
env')
-
- // also avoid replacing vite user defines
- if (userDefineRegex) {
- html = html.replace(
- userDefineRegex,
- (_) => `${_[0]}
${_.slice(1)}`
- )
- }
- }
-
// validate data.links
const deadLinks: string[] = []
const recordDeadLink = (url: string) => {
@@ -150,8 +128,14 @@ export async function createMarkdownToVueRenderFn(
}
const vueSrc =
- genPageDataCode(data.hoistedTags || [], pageData).join('\n') +
- `\n
${html}
`
+ genPageDataCode(data.hoistedTags || [], pageData, replaceRegex).join(
+ '\n'
+ ) +
+ `\n
${replaceConstants(
+ html,
+ replaceRegex,
+ vueTemplateBreaker
+ )}
`
debug(`[render] ${file} in ${Date.now() - start}ms.`)
@@ -172,10 +156,42 @@ const scriptSetupRE = /<\s*script[^>]*\bsetup\b[^>]*/
const scriptClientRE = /<\s*script[^>]*\bclient\b[^>]*/
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/
+const jsStringBreaker = '\u200b'
+const vueTemplateBreaker = '
'
+
+function genReplaceRegexp(
+ userDefines: Record = {},
+ isBuild: boolean
+): RegExp {
+ // `process.env` need to be handled in both dev and build
+ // @see https://github.com/vitejs/vite/blob/cad27ee8c00bbd5aeeb2be9bfb3eb164c1b77885/packages/vite/src/node/plugins/clientInjections.ts#L57-L64
+ const replacements = ['process.env']
+ if (isBuild) {
+ replacements.push('import.meta', ...Object.keys(userDefines))
+ }
+ return new RegExp(
+ `\\b(${replacements
+ .map((key) => key.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'))
+ .join('|')})`,
+ 'g'
+ )
+}
+
+/**
+ * To avoid env variables being replaced by vite:
+ * - insert `'\u200b'` char into those strings inside js string (page data)
+ * - insert `` tag into those strings inside html string (vue template)
+ *
+ * @see https://vitejs.dev/guide/env-and-mode.html#production-replacement
+ */
+function replaceConstants(str: string, replaceRegex: RegExp, breaker: string) {
+ return str.replace(replaceRegex, (_) => `${_[0]}${breaker}${_.slice(1)}`)
+}
-function genPageDataCode(tags: string[], data: PageData) {
+function genPageDataCode(tags: string[], data: PageData, replaceRegex: RegExp) {
+ const dataJson = JSON.stringify(data)
const code = `\nexport const __pageData = JSON.parse(${JSON.stringify(
- JSON.stringify(data)
+ replaceConstants(dataJson, replaceRegex, jsStringBreaker)
)})`
const existingScriptIndex = tags.findIndex((tag) => {
diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts
index f19f4395..2faa0889 100644
--- a/types/default-theme.d.ts
+++ b/types/default-theme.d.ts
@@ -43,6 +43,11 @@ export namespace DefaultTheme {
*/
lastUpdatedText?: string
+ /**
+ * Set custom prev/next labels.
+ */
+ docFooter?: DocFooter
+
/**
* The social links to be displayed at the end of the nav bar. Perfect for
* placing links to social services such as GitHub, Twitter, Facebook, etc.
@@ -157,6 +162,24 @@ export namespace DefaultTheme {
text?: string
}
+ // prev-next -----------------------------------------------------------------
+
+ export interface DocFooter {
+ /**
+ * Custom label for previous page button.
+ *
+ * @default 'Previous page'
+ */
+ prev?: string
+
+ /**
+ * Custom label for next page button.
+ *
+ * @default 'Next page'
+ */
+ next?: string
+ }
+
// social link ---------------------------------------------------------------
export interface SocialLink {