Merge branch 'main' into zh

pull/1593/head
Xavi Lee 4 years ago committed by GitHub
commit b7e86c36b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,2 +1 @@
shell-emulator=true
update-notifier=false

@ -1,3 +1,42 @@
# [1.0.0-alpha.43](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.42...v1.0.0-alpha.43) (2023-01-29)
### Bug Fixes
* **build:** hmr with rewrites when base is set ([a05956f](https://github.com/vuejs/vitepress/commit/a05956f38a5295cf038ecfc762c044dbc1cdf040))
# [1.0.0-alpha.42](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.41...v1.0.0-alpha.42) (2023-01-29)
### Bug Fixes
* **build:** consider base when checking actual pathname ([#1858](https://github.com/vuejs/vitepress/issues/1858)) ([cf8ad1a](https://github.com/vuejs/vitepress/commit/cf8ad1a29133cc373a1a70720d36e02b38ba6898))
# [1.0.0-alpha.41](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.40...v1.0.0-alpha.41) (2023-01-28)
### Bug Fixes
* check document instead of window to detect browser ([#1833](https://github.com/vuejs/vitepress/issues/1833)) ([0f145cb](https://github.com/vuejs/vitepress/commit/0f145cb3c6568760199a9c8eee785aecaf0e0494))
* **router:** avoid duplicate history entries ([#1827](https://github.com/vuejs/vitepress/issues/1827)) ([1553dbc](https://github.com/vuejs/vitepress/commit/1553dbce8eac9ed4a65312d4590d6b0f9261135c))
* **theme:** don't show border on navbar when sidebar is there ([#1845](https://github.com/vuejs/vitepress/issues/1845)) ([3db532e](https://github.com/vuejs/vitepress/commit/3db532ed0999c9bddfd6bc90f6b627ae1b9178af))
### Features
* **build:** allow ignoring only localhost dead links ([#1821](https://github.com/vuejs/vitepress/issues/1821)) ([fe52fa3](https://github.com/vuejs/vitepress/commit/fe52fa34201dcfa87ac4886fe285331f0ef89ba8))
* **build:** expose vitepress site config to vite plugins ([#1822](https://github.com/vuejs/vitepress/issues/1822)) ([05430e4](https://github.com/vuejs/vitepress/commit/05430e45c90562b62796caba28c633070934d85f))
* **build:** support rewrites ([#1798](https://github.com/vuejs/vitepress/issues/1798)) ([00abac6](https://github.com/vuejs/vitepress/commit/00abac611664e12710e5152d0259390b22c0e8ca))
* stable `cleanUrls` ([#1852](https://github.com/vuejs/vitepress/issues/1852)) ([5ae4fbd](https://github.com/vuejs/vitepress/commit/5ae4fbde3843236e180e63e2cd2b7021efa0fad4))
* **theme:** allow removing badge text from outline ([#1825](https://github.com/vuejs/vitepress/issues/1825)) ([5d2fc3f](https://github.com/vuejs/vitepress/commit/5d2fc3f9228c9b26dec26264d0951d0f43b3d90d))
* **theme:** enable multi level sidebar nesting ([#1360](https://github.com/vuejs/vitepress/issues/1360)) ([#1835](https://github.com/vuejs/vitepress/issues/1835)) ([c35a1f0](https://github.com/vuejs/vitepress/commit/c35a1f0faee3702c0494fb22043ce058e7a2954c)), closes [#1361](https://github.com/vuejs/vitepress/issues/1361) [#1680](https://github.com/vuejs/vitepress/issues/1680)
# [1.0.0-alpha.40](https://github.com/vuejs/vitepress/compare/v1.0.0-alpha.39...v1.0.0-alpha.40) (2023-01-20)

@ -9,7 +9,7 @@ export default defineConfig({
title: 'VitePress',
lastUpdated: true,
cleanUrls: 'without-subfolders',
cleanUrls: true,
head: [['meta', { name: 'theme-color', content: '#3c8772' }]],

@ -132,7 +132,7 @@ Configure Markdown parser options. VitePress uses [Markdown-it](https://github.c
```js
export default {
markdown: {
theme: 'material-palenight',
theme: 'material-theme-palenight',
lineNumbers: true
}
}
@ -266,28 +266,34 @@ export default {
}
```
## cleanUrls (Experimental)
## cleanUrls
- Type: `'disabled' | 'without-subfolders' | 'with-subfolders'`
- Default: `'disabled'`
- Type: `boolean`
- Default: `false`
Allows removing trailing `.html` from URLs and, optionally, generating clean directory structure. Available modes:
Allows removing trailing `.html` from URLs.
| Mode | Page | Generated Page | URL |
| :--------------------: | :-------: | :---------------: | :---------: |
| `'disabled'` | `/foo.md` | `/foo.html` | `/foo.html` |
| `'without-subfolders'` | `/foo.md` | `/foo.html` | `/foo` |
| `'with-subfolders'` | `/foo.md` | `/foo/index.html` | `/foo` |
```ts
export default {
cleanUrls: true
}
```
::: warning
Enabling this may require additional configuration on your hosting platform. For it to work, your server must serve `/foo.html` on requesting `/foo` **without a redirect**.
:::
Enabling this may require additional configuration on your hosting platform. For it to work, your server must serve the generated page on requesting the URL (see above table) **without a redirect**.
## rewrites
:::
- Type: `Record<string, string>`
Defines custom directory <-> URL mappings. See [Routing: Customize the Mappings](/guide/routing#customize-the-mappings) for more details.
```ts
export default {
cleanUrls: 'with-subfolders'
rewrites: {
'source/:page': 'destination/:page'
}
}
```

@ -8,7 +8,7 @@ You can also try VitePress online on [StackBlitz](https://vitepress.new/). It ru
VitePress is currently in `alpha` status. It is already suitable for out-of-the-box documentation use, but the config and theming API may still change between minor releases.
:::
## Step. 1: Create a new project
## Step 1: Create a new project
Create and change into a new directory.
@ -22,7 +22,7 @@ Then, initialize with your preferred package manager.
$ yarn init
```
## Step. 2: Install VitePress
## Step 2: Install VitePress
Add VitePress and Vue as dev dependencies for the project.
@ -53,7 +53,7 @@ Create your first document.
$ mkdir docs && echo '# Hello VitePress' > docs/index.md
```
## Step. 3: Boot up dev environment
## Step 3: Boot up dev environment
Add some scripts to `package.json`.
@ -77,9 +77,9 @@ $ yarn docs:dev
VitePress will start a hot-reloading development server at `http://localhost:5173`.
## Step. 4: Add more pages
## Step 4: Add more pages
Let's add another page to the site. Create a file name `getting-started.md` along with `index.md` you've created in Step. 2. Now your directory structure should look like this.
Let's add another page to the site. Create a file name `getting-started.md` along with `index.md` you've created in Step 2. Now your directory structure should look like this.
```
.

@ -0,0 +1,173 @@
# Routing
VitePress is built with file system based routing, which means the directory structure of the source file corresponds to the final URL. You may customize the mapping of the directory structure and URL too. Read through this page to learn everything about the VitePress routing system.
## Basic Routing
By default, VitePress assumes your page files are stored in project root. Here you may add markdown files with the name being the URL path. For example, when you have following directory structure:
```
.
├─ guide
│ ├─ getting-started.md
│ └─ index.md
├─ index.md
└─ prologue.md
```
Then you can access the pages by the below URL.
```
index.md -> /
prologue.md -> /prologue.html
guide/index.md -> /guide/
getting-started.md -> /guide/getting-started.html
```
As you can see, the directory structure corresponds to the final URL, as same as hosting plain HTML from a typical web server.
## Changing the Root Directory
To change the root directory for your page files, you may pass the directory name to the `vitepress` command. For example, if you want to store your page files under `docs` directory, then you should run `vitepress dev docs` command.
```
.
├─ docs
│ ├─ getting-started.md
│ └─ index.md
└─ ...
```
```
vitepress dev docs
```
This is going to map the URL as follows.
```
docs/index.md -> /
docs/getting-started.md -> /getting-started.html
```
You may also customize the root directory in config file via [`srcDir`](/config/app-configs#srcdir) option too. Running `vitepress dev` with the following setting acts same as running `vitepress dev docs` command.
```ts
export default {
srcDir: './docs'
}
```
## Linking Between Pages
When adding links in pages, omit extension from the path and use either absolute path from the root, or relative path from the page. VitePress will handle the extension according to your configuration setup.
```md
<!-- Do -->
[Getting Started](/guide/getting-started)
[Getting Started](../guide/getting-started)
<!-- Don't -->
[Getting Started](/guide/getting-started.md)
[Getting Started](/guide/getting-started.html)
```
Learn more about page links and links to assets, such as link to images, at [Asset Handling](asset-handling).
## Generate Clean URL
A "Clean URL" is commonly known as URL without `.html` extension, for example, `example.com/path` instead of `example.com/path.html`.
By default, VitePress generates the final static page files by adding `.html` extension to each file. If you would like to have clean URL, you may structure your directory by only using `index.html` file.
```
.
├─ getting-started
│ └─ index.md
├─ installation
│ └─ index.md
└─ index.md
```
However, you may also generate a clean URL by setting up [`cleanUrls`](/config/app-configs#cleanurls) option.
```ts
export default {
cleanUrls: true
}
```
## Customize the Mappings
You may customize the mapping between directory structure and URL. It's useful when you have complex document structure. For example, let's say you have several packages and would like to place documentations along with the source files like this.
```
.
├─ packages
│ ├─ pkg-a
│ │ └─ src
│ │ ├─ pkg-a-code.ts
│ │ └─ pkg-a-code.md
│ └─ pkg-b
│ └─ src
│ ├─ pkg-b-code.ts
│ └─ pkg-b-code.md
```
And you want the VitePress pages to be generated as follows.
```
packages/pkg-a/src/pkg-a-code.md -> /pkg-a/pkg-a-code.md
packages/pkg-b/src/pkg-b-code.md -> /pkg-b/pkg-b-code.md
```
You may configure the mapping via [`rewrites`](/config/app-configs#rewrites) option like this.
```ts
export default {
rewrites: {
'packages/pkg-a/src/pkg-a-code.md': 'pkg-a/pkg-a-code',
'packages/pkg-b/src/pkg-b-code.md': 'pkg-b/pkg-b-code'
}
}
```
The `rewrites` option can also have dynamic route parameters. In this example, we have fixed path `packages` and `src` which stays the same on all pages, and it might be verbose to have to list all pages in your config as you add pages. You may configure the above mapping as below and get the same result.
```ts
export default {
rewrites: {
'packages/:pkg/src/:page': ':pkg/:page'
}
}
```
Route parameters are prefixed by `:` (e.g. `:pkg`). The name of the parameter is just a placeholder and can be anything.
In addition, you may add `*` at the end of the parameter to map all sub directories from there on.
```ts
export default {
rewrites: {
'packages/:pkg/src/:page*': ':pkg/:page*'
}
}
```
The above will create mapping as below.
```
packages/pkg-a/src/pkg-a-code.md -> /pkg-a/pkg-a-code
packages/pkg-b/src/folder/file.md -> /pkg-b/folder/file
```
::: warning You need server restart on page addition
At the moment, VitePress doesn't detect page additions to the mapped directory. You need to restart your server when adding or removing files from the directory during the dev mode. Updating the already existing files gets updated as usual.
:::
### Relative Link Handling in Page
Note that when enabling rewrites, **relative links in the markdown are resolved relative to the final path**. For example, in order to create relative link from `packages/pkg-a/src/pkg-a-code.md` to `packages/pkg-b/src/pkg-b-code.md`, you should define link as below.
```md
[Link to PKG B](../pkg-b/pkg-b-code)
```

@ -1,9 +1,9 @@
{
"name": "vitepress",
"version": "1.0.0-alpha.40",
"version": "1.0.0-alpha.43",
"description": "Vite & Vue powered static site generator",
"type": "module",
"packageManager": "pnpm@7.9.2",
"packageManager": "pnpm@7.26.1",
"main": "dist/node/index.js",
"types": "types/index.d.ts",
"exports": {
@ -86,7 +86,7 @@
"@vue/devtools-api": "^6.5.0",
"@vueuse/core": "^9.11.1",
"body-scroll-lock": "4.0.0-beta.0",
"shiki": "^0.12.1",
"shiki": "^0.13.0",
"vite": "^4.0.4",
"vue": "^3.2.45"
},
@ -98,7 +98,7 @@
"@mdit-vue/plugin-title": "^0.11.2",
"@mdit-vue/plugin-toc": "^0.11.2",
"@mdit-vue/shared": "^0.11.2",
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-alias": "^4.0.3",
"@rollup/plugin-commonjs": "23.0.4",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
@ -125,7 +125,7 @@
"cross-spawn": "^7.0.3",
"debug": "^4.3.4",
"enquirer": "^2.3.6",
"esbuild": "^0.17.3",
"esbuild": "^0.17.5",
"escape-html": "^1.0.3",
"execa": "^6.1.0",
"fast-glob": "^3.2.12",
@ -143,15 +143,16 @@
"nanoid": "3.3.4",
"npm-run-all": "^4.1.5",
"ora": "5.4.1",
"path-to-regexp": "^6.2.1",
"picocolors": "^1.0.0",
"pkg-dir": "5.0.0",
"playwright-chromium": "^1.29.2",
"playwright-chromium": "^1.30.0",
"polka": "1.0.0-next.22",
"prettier": "^2.8.3",
"prompts": "^2.4.2",
"punycode": "^2.3.0",
"rimraf": "^4.1.1",
"rollup": "^3.10.1",
"rimraf": "^4.1.2",
"rollup": "^3.12.0",
"rollup-plugin-dts": "^5.1.1",
"rollup-plugin-esbuild": "^5.0.0",
"semver": "^7.3.8",
@ -160,7 +161,7 @@
"sirv": "^2.0.2",
"supports-color": "^9.3.1",
"typescript": "~4.9.4",
"vitest": "^0.27.2",
"vitest": "^0.28.3",
"vue-tsc": "^1.0.24",
"wait-on": "^7.0.1"
},

File diff suppressed because it is too large Load Diff

@ -73,32 +73,40 @@ export function usePrefetch() {
})
rIC(() => {
document.querySelectorAll<HTMLAnchorElement>('#app a').forEach((link) => {
const { target, hostname, pathname } = link
const extMatch = pathname.match(/\.\w+$/)
if (extMatch && extMatch[0] !== '.html') {
return
}
document
.querySelectorAll<HTMLAnchorElement | SVGAElement>('#app a')
.forEach((link) => {
const { target } = link
const { hostname, pathname } = new URL(
link.href instanceof SVGAnimatedString
? link.href.animVal
: link.href,
link.baseURI
)
const extMatch = pathname.match(/\.\w+$/)
if (extMatch && extMatch[0] !== '.html') {
return
}
if (
// only prefetch same tab navigation, since a new tab will load
// the lean js chunk instead.
target !== `_blank` &&
// only prefetch inbound links
hostname === location.hostname
) {
if (pathname !== location.pathname) {
observer!.observe(link)
} else {
// No need to prefetch chunk for the current page, but also mark
// it as already fetched. This is because the initial page uses its
// lean chunk, and if we don't mark it, navigation to another page
// with a link back to the first page will fetch its full chunk
// which isn't needed.
hasFetched.add(pathname)
if (
// only prefetch same tab navigation, since a new tab will load
// the lean js chunk instead.
target !== `_blank` &&
// only prefetch inbound links
hostname === location.hostname
) {
if (pathname !== location.pathname) {
observer!.observe(link)
} else {
// No need to prefetch chunk for the current page, but also mark
// it as already fetched. This is because the initial page uses its
// lean chunk, and if we don't mark it, navigation to another page
// with a link back to the first page will fetch its full chunk
// which isn't needed.
hasFetched.add(pathname)
}
}
}
})
})
})
}

@ -49,7 +49,7 @@ export function createRouter(
async function go(href: string = inBrowser ? location.href : '/') {
await router.onBeforeRouteChange?.(href)
const url = new URL(href, fakeHost)
if (siteDataRef.value.cleanUrls === 'disabled') {
if (!siteDataRef.value.cleanUrls) {
// ensure correct deep link so page refresh lands on correct files.
// if cleanUrls is enabled, the server should handle this
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
@ -89,6 +89,18 @@ export function createRouter(
if (inBrowser) {
nextTick(() => {
let actualPathname =
siteDataRef.value.base +
__pageData.relativePath.replace(/(?:(^|\/)index)?\.md$/, '$1')
if (!siteDataRef.value.cleanUrls && !actualPathname.endsWith('/')) {
actualPathname += '.html'
}
if (actualPathname !== targetLoc.pathname) {
targetLoc.pathname = actualPathname
href = actualPathname + targetLoc.search + targetLoc.hash
history.replaceState(null, '', href)
}
if (targetLoc.hash && !scrollPosition) {
let target: HTMLElement | null = null
try {
@ -141,9 +153,21 @@ export function createRouter(
const button = (e.target as Element).closest('button')
if (button) return
const link = (e.target as Element).closest('a')
if (link && !link.closest('.vp-raw') && !link.download) {
const { href, origin, pathname, hash, search, target } = link
const link = (e.target as Element | SVGElement).closest<
HTMLAnchorElement | SVGAElement
>('a')
if (
link &&
!link.closest('.vp-raw') &&
(link instanceof SVGElement || !link.download)
) {
const { target } = link
const { href, origin, pathname, hash, search } = new URL(
link.href instanceof SVGAnimatedString
? link.href.animVal
: link.href,
link.baseURI
)
const currentUrl = window.location
const extMatch = pathname.match(/\.\w+$/)
// only intercept inbound links
@ -205,8 +229,8 @@ export function useRoute(): Route {
return useRouter().route
}
function scrollTo(el: HTMLElement, hash: string, smooth = false) {
let target: HTMLElement | null = null
function scrollTo(el: HTMLElement | SVGElement, hash: string, smooth = false) {
let target: HTMLElement | SVGElement | null = null
try {
target = el.classList.contains('header-anchor')

@ -90,7 +90,7 @@ function getRelativePath(absoluteUrl: string) {
return (
pathname.replace(
/\.html$/,
site.value.cleanUrls === 'disabled' ? '.html' : ''
site.value.cleanUrls ? '' : '.html'
) + hash
)
}

@ -82,14 +82,10 @@ const classes = computed(() => ({
padding: 0;
}
.VPNavBar.fill {
.VPNavBar.fill:not(.has-sidebar) {
border-bottom-color: var(--vp-c-gutter);
background-color: var(--vp-nav-bg-color);
}
.VPNavBar.has-sidebar.fill {
background-color: transparent;
}
}
.container {

@ -78,6 +78,22 @@ function poll() {
}
}, 16)
}
onMounted(() => {
const id = 'VPAlgoliaPreconnect'
const rIC = requestIdleCallback || setTimeout
rIC(() => {
if (!theme.value.algolia || document.head.querySelector(`#${id}`)) return
const preconnect = document.createElement('link')
preconnect.id = id
preconnect.rel = 'preconnect'
preconnect.href = `https://${theme.value.algolia.appId}-dsn.algolia.net`
preconnect.crossOrigin = ''
document.head.appendChild(preconnect)
})
})
</script>
<template>

@ -24,7 +24,7 @@ export function useLangs({
value.link || (key === 'root' ? '/' : `/${key}/`),
theme.value.i18nRouting !== false && correspondingLink,
page.value.relativePath.slice(currentLang.value.link.length - 1),
site.value.cleanUrls === 'disabled'
!site.value.cleanUrls
)
}
)

@ -44,7 +44,7 @@ export function normalizeLink(url: string): string {
/(?:(^\.+)\/)?.*$/,
`$1${pathname.replace(
/(\.md)?$/,
site.value.cleanUrls === 'disabled' ? '.html' : ''
site.value.cleanUrls ? '' : '.html'
)}${search}${hash}`
)

@ -64,7 +64,9 @@ export async function build(
// as JS object literal.
const hashMapString = JSON.stringify(JSON.stringify(pageToHashMap))
const pages = ['404.md', ...siteConfig.pages]
const pages = ['404.md', ...siteConfig.pages].map(
(page) => siteConfig.rewrites.map[page] || page
)
await Promise.all(
pages.map((page) =>

@ -37,7 +37,8 @@ export async function bundle(
config.pages.forEach((file) => {
// page filename conversion
// foo/bar.md -> foo_bar.md
input[slash(file).replace(/\//g, '_')] = path.resolve(config.srcDir, file)
const alias = config.rewrites.map[file] || file
input[slash(alias).replace(/\//g, '_')] = path.resolve(config.srcDir, file)
})
// resolve options to pass to vite

@ -173,14 +173,7 @@ export async function renderPage(
${inlinedScript}
</body>
</html>`.trim()
const createSubDirectory =
config.cleanUrls === 'with-subfolders' &&
!/(^|\/)(index|404).md$/.test(page)
const htmlFileName = path.join(
config.outDir,
page.replace(/\.md$/, createSubDirectory ? '/index.html' : '.html')
)
const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html'))
await fs.ensureDir(path.dirname(htmlFileName))
const transformedHtml = await config.transformHtml?.(html, htmlFileName, {
@ -201,6 +194,7 @@ function resolvePageImports(
result: RollupOutput,
appChunk: OutputChunk
) {
page = config.rewrites.inv[page] || page
// find the page's js chunk and inject script tags for its imports so that
// they start fetching as early as possible
const srcPath = normalizePath(

@ -3,6 +3,7 @@ import _debug from 'debug'
import fg from 'fast-glob'
import fs from 'fs-extra'
import path from 'path'
import { match, compile } from 'path-to-regexp'
import c from 'picocolors'
import {
loadConfigFromFile,
@ -15,7 +16,6 @@ import type { MarkdownOptions } from './markdown/markdown'
import {
APPEARANCE_KEY,
type Awaitable,
type CleanUrlsMode,
type DefaultTheme,
type HeadConfig,
type LocaleConfig,
@ -76,17 +76,11 @@ export interface UserConfig<ThemeConfig = any>
ignoreDeadLinks?: boolean | 'localhostLinks'
/**
* @experimental
* Remove '.html' from URLs and generate clean directory structure.
*
* Available Modes:
* - `disabled`: generates `/foo.html` for every `/foo.md` and shows `/foo.html` in browser
* - `without-subfolders`: generates `/foo.html` for every `/foo.md` but shows `/foo` in browser
* - `with-subfolders`: generates `/foo/index.html` for every `/foo.md` and shows `/foo` in browser
* Don't force `.html` on URLs.
*
* @default 'disabled'
* @default false
*/
cleanUrls?: CleanUrlsMode
cleanUrls?: boolean
/**
* Use web fonts instead of emitting font files to dist.
@ -98,6 +92,13 @@ export interface UserConfig<ThemeConfig = any>
*/
useWebFonts?: boolean
/**
* @experimental
*
* source -> destination
*/
rewrites?: Record<string, string>
/**
* Build end hook: called when SSG finish.
* @param siteConfig The resolved configuration.
@ -175,6 +176,10 @@ export interface SiteConfig<ThemeConfig = any>
cacheDir: string
tempDir: string
pages: string[]
rewrites: {
map: Record<string, string | undefined>
inv: Record<string, string | undefined>
}
}
const resolve = (root: string, file: string) =>
@ -234,6 +239,21 @@ export async function resolveConfig(
})
).sort()
const rewriteEntries = Object.entries(userConfig.rewrites || {})
const rewrites = rewriteEntries.length
? Object.fromEntries(
pages
.map((src) => {
for (const [from, to] of rewriteEntries) {
const dest = rewrite(src, from, to)
if (dest) return [src, dest]
}
})
.filter((e) => e != null) as [string, string][]
)
: {}
const config: SiteConfig = {
root,
srcDir,
@ -252,7 +272,7 @@ export async function resolveConfig(
shouldPreload: userConfig.shouldPreload,
mpa: !!userConfig.mpa,
ignoreDeadLinks: userConfig.ignoreDeadLinks,
cleanUrls: userConfig.cleanUrls || 'disabled',
cleanUrls: !!userConfig.cleanUrls,
useWebFonts:
userConfig.useWebFonts ??
typeof process.versions.webcontainer === 'string',
@ -260,7 +280,11 @@ export async function resolveConfig(
buildEnd: userConfig.buildEnd,
transformHead: userConfig.transformHead,
transformHtml: userConfig.transformHtml,
transformPageData: userConfig.transformPageData
transformPageData: userConfig.transformPageData,
rewrites: {
map: rewrites,
inv: Object.fromEntries(Object.entries(rewrites).map((a) => a.reverse()))
}
}
return config
@ -363,7 +387,7 @@ export async function resolveSiteData(
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
scrollOffset: userConfig.scrollOffset || 90,
cleanUrls: userConfig.cleanUrls || 'disabled'
cleanUrls: !!userConfig.cleanUrls
}
}
@ -395,3 +419,11 @@ function resolveSiteDataHead(userConfig?: UserConfig): HeadConfig[] {
return head
}
function rewrite(src: string, from: string, to: string) {
const urlMatch = match(from)
const res = urlMatch(src)
if (!res) return false
const toPath = compile(to)
return toPath(res.params)
}

@ -1,5 +1,5 @@
import type { MarkdownSfcBlocks } from '@mdit-vue/plugin-sfc'
import type { CleanUrlsMode, Header } from '../shared'
import type { Header } from '../shared'
// Manually declaring all properties as rollup-plugin-dts
// is unable to merge augmented module declarations
@ -34,6 +34,6 @@ export interface MarkdownEnv {
title?: string
path: string
relativePath: string
cleanUrls: CleanUrlsMode
cleanUrls: boolean
links?: string[]
}

@ -56,7 +56,7 @@ const errorLevelProcessor = defineProcessor({
})
export async function highlight(
theme: ThemeOptions = 'material-palenight',
theme: ThemeOptions = 'material-theme-palenight',
defaultLang: string = ''
): Promise<(str: string, lang: string, attrs: string) => string> {
const hasSingleTheme = typeof theme === 'string' || 'name' in theme

@ -68,14 +68,11 @@ export const linkPlugin = (
let cleanUrl = url.replace(/[?#].*$/, '')
// transform foo.md -> foo[.html]
if (cleanUrl.endsWith('.md')) {
cleanUrl = cleanUrl.replace(
/\.md$/,
env.cleanUrls === 'disabled' ? '.html' : ''
)
cleanUrl = cleanUrl.replace(/\.md$/, env.cleanUrls ? '' : '.html')
}
// transform ./foo -> ./foo[.html]
if (
env.cleanUrls === 'disabled' &&
!env.cleanUrls &&
!cleanUrl.endsWith('.html') &&
!cleanUrl.endsWith('/')
) {

@ -4,12 +4,7 @@ import c from 'picocolors'
import LRUCache from 'lru-cache'
import { resolveTitleFromToken } from '@mdit-vue/shared'
import type { SiteConfig } from './config'
import {
type PageData,
type HeadConfig,
EXTERNAL_URL_RE,
type CleanUrlsMode
} from './shared'
import { type PageData, type HeadConfig, EXTERNAL_URL_RE } from './shared'
import { slash } from './utils/slash'
import { getGitTimestamp } from './utils/getGitTimestamp'
import {
@ -43,7 +38,7 @@ export async function createMarkdownToVueRenderFn(
isBuild = false,
base = '/',
includeLastUpdatedData = false,
cleanUrls: CleanUrlsMode = 'disabled',
cleanUrls = false,
siteConfig: SiteConfig | null = null
) {
const md = await createMarkdownRenderer(srcDir, options, base)
@ -55,6 +50,8 @@ export async function createMarkdownToVueRenderFn(
file: string,
publicDir: string
): Promise<MarkdownCompileResult> => {
const alias = siteConfig?.rewrites.map[file.slice(srcDir.length + 1)]
file = alias ? path.join(srcDir, alias) : file
const relativePath = slash(path.relative(srcDir, file))
const dir = path.dirname(file)
const cacheKey = JSON.stringify({ src, file })
@ -125,13 +122,15 @@ export async function createMarkdownToVueRenderFn(
url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '')
if (url.endsWith('/')) url += `index`
const resolved = decodeURIComponent(
let resolved = decodeURIComponent(
slash(
url.startsWith('/')
? url.slice(1)
: path.relative(srcDir, path.resolve(dir, url))
)
)
resolved =
siteConfig?.rewrites.inv[resolved + '.md']?.slice(0, -3) || resolved
if (
!pages.includes(resolved) &&
!fs.existsSync(path.resolve(dir, publicDir, `${resolved}.html`))

@ -63,7 +63,8 @@ export async function createVitePressPlugin(
pages,
ignoreDeadLinks,
lastUpdated,
cleanUrls
cleanUrls,
rewrites
} = siteConfig
let markdownToVue: Awaited<ReturnType<typeof createMarkdownToVueRenderFn>>
@ -191,6 +192,14 @@ export async function createVitePressPlugin(
configDeps.forEach((file) => server.watcher.add(file))
}
server.middlewares.use((req, res, next) => {
if (req.url) {
const page = req.url.replace(/[?#].*$/, '').slice(site.base.length)
req.url = req.url.replace(page, rewrites.inv[page] || page)
}
next()
})
// serve our index.html after vite history fallback
return () => {
server.middlewares.use(async (req, res, next) => {

@ -2,7 +2,6 @@ import type { HeadConfig, PageData, SiteData } from '../../types/shared.js'
export type {
Awaitable,
CleanUrlsMode,
DefaultTheme,
HeadConfig,
Header,

7
types/shared.d.ts vendored

@ -43,14 +43,9 @@ export interface Header {
children: Header[]
}
export type CleanUrlsMode =
| 'disabled'
| 'without-subfolders'
| 'with-subfolders'
export interface SiteData<ThemeConfig = any> {
base: string
cleanUrls?: CleanUrlsMode
cleanUrls?: boolean
lang: string
dir: string
title: string

Loading…
Cancel
Save