feat(cleanUrls): Generate "clean urls" in SPA and MPA mode

pull/487/head
Georges Gomes 4 years ago
parent 51978a3ff5
commit 670498804e

@ -1,3 +1,4 @@
export default {
lang: 'en-US',
title: 'VitePress',

@ -2,6 +2,7 @@ import { reactive, inject, markRaw, nextTick, readonly } from 'vue'
import type { Component, InjectionKey } from 'vue'
import { PageData } from '../shared'
import { inBrowser } from './utils'
import { siteDataRef } from './data'
export interface Route {
path: string
@ -42,10 +43,15 @@ export function createRouter(
function go(href: string = inBrowser ? location.href : '/') {
// ensure correct deep link so page refresh lands on correct files.
const url = new URL(href, fakeHost)
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
url.pathname += '.html'
href = url.pathname + url.search + url.hash
if (siteDataRef.value.cleanUrls) {
// TODO
}
else {
const url = new URL(href, fakeHost)
if (!url.pathname.endsWith('/') && !url.pathname.endsWith('.html')) {
url.pathname += '.html'
href = url.pathname + url.search + url.hash
}
}
if (inBrowser) {
// save scroll position before changing url

@ -138,11 +138,19 @@ export async function renderPage(
${inlinedScript}
</body>
</html>`.trim()
const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html'))
const htmlFileName = path.join(config.outDir, transformHTMLFileName(page, config.cleanUrls));
await fs.ensureDir(path.dirname(htmlFileName))
await fs.writeFile(htmlFileName, html)
}
function transformHTMLFileName(page: string, shouldCleanUrls: boolean): string {
if (page === 'index.md' || page.endsWith('/index.md')) {
return page.replace(/\.md$/, '.html');
}
return page.replace(/\.md$/, shouldCleanUrls ? '/index.html' : '.html');
}
function resolvePageImports(
config: SiteConfig,
page: string,

@ -54,6 +54,14 @@ export interface UserConfig<ThemeConfig = any> {
* @experimental
*/
mpa?: boolean
/**
* Always use "clean URLs" without the `.html`.
* Also generate static files as `foo/index.html` insted of `foo.html`.
* (default: false)
* @experimental (Works better with mpa mode)
*/
cleanUrls?: boolean
}
export type RawConfigExports =
@ -73,6 +81,7 @@ export interface SiteConfig<ThemeConfig = any>
themeDir: string
outDir: string
tempDir: string
cleanUrls: boolean;
alias: AliasOptions
pages: string[]
}
@ -124,6 +133,7 @@ export async function resolveConfig(
configPath,
outDir: resolve(root, 'dist'),
tempDir: path.resolve(APP_PATH, 'temp'),
cleanUrls: !!userConfig.cleanUrls,
markdown: userConfig.markdown,
alias: resolveAliases(themeDir),
vue: userConfig.vue,
@ -229,6 +239,7 @@ export async function resolveSiteData(
head: userConfig.head || [],
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
langs: createLangDictionary(userConfig)
langs: createLangDictionary(userConfig),
cleanUrls: userConfig.cleanUrls || false
}
}

@ -16,6 +16,7 @@ import anchor from 'markdown-it-anchor'
import attrs from 'markdown-it-attrs'
import emoji from 'markdown-it-emoji'
import toc from 'markdown-it-table-of-contents'
import { SiteConfig } from 'config'
export interface MarkdownOptions extends MarkdownIt.Options {
lineNumbers?: boolean
@ -47,7 +48,7 @@ export interface MarkdownRenderer {
export type { Header }
export const createMarkdownRenderer = (
srcDir: string,
siteConfig: SiteConfig,
options: MarkdownOptions = {}
): MarkdownRenderer => {
const md = MarkdownIt({
@ -61,7 +62,7 @@ export const createMarkdownRenderer = (
md.use(componentPlugin)
.use(highlightLinePlugin)
.use(preWrapperPlugin)
.use(snippetPlugin, srcDir)
.use(snippetPlugin, siteConfig.srcDir)
.use(hoistPlugin)
.use(containerPlugin)
.use(extractHeaderPlugin)
@ -69,7 +70,7 @@ export const createMarkdownRenderer = (
target: '_blank',
rel: 'noopener noreferrer',
...options.externalLinks
})
}, siteConfig.cleanUrls)
// 3rd party plugins
.use(attrs, options.attrs)
.use(anchor, {

@ -11,7 +11,8 @@ const indexRE = /(^|.*\/)index.md(#?.*)$/i
export const linkPlugin = (
md: MarkdownIt,
externalAttrs: Record<string, string>
externalAttrs: Record<string, string>,
shouldCleanUrls: boolean
) => {
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const token = tokens[idx]
@ -30,7 +31,7 @@ export const linkPlugin = (
// mail links
!url.startsWith('mailto:')
) {
normalizeHref(hrefAttr)
normalizeHref(hrefAttr, shouldCleanUrls)
}
// encode vite-specific replace strings in case they appear in URLs
@ -43,7 +44,7 @@ export const linkPlugin = (
return self.renderToken(tokens, idx, options)
}
function normalizeHref(hrefAttr: [string, string]) {
function normalizeHref(hrefAttr: [string, string], shouldCleanUrls: boolean) {
let url = hrefAttr[1]
const indexMatch = url.match(indexRE)
@ -52,12 +53,12 @@ export const linkPlugin = (
url = path + hash
} else {
let cleanUrl = url.replace(/\#.*$/, '').replace(/\?.*$/, '')
// .md -> .html
// transform foo.md -> foo[.html]
if (cleanUrl.endsWith('.md')) {
cleanUrl = cleanUrl.replace(/\.md$/, '.html')
cleanUrl = cleanUrl.replace(/\.md$/, shouldCleanUrls ? '' : '.html')
}
// ./foo -> ./foo.html
if (!cleanUrl.endsWith('.html') && !cleanUrl.endsWith('/')) {
// transform ./foo -> ./foo[.html]
if (!shouldCleanUrls && !cleanUrl.endsWith('.html') && !cleanUrl.endsWith('/')) {
cleanUrl += '.html'
}
const parsed = new URL(url, 'http://a.com')

@ -8,6 +8,7 @@ import { PageData, HeadConfig } from './shared'
import { slash } from './utils/slash'
import chalk from 'chalk'
import _debug from 'debug'
import { SiteConfig } from 'config'
const debug = _debug('vitepress:md')
const cache = new LRUCache<string, MarkdownCompileResult>({ max: 1024 })
@ -21,13 +22,15 @@ export interface MarkdownCompileResult {
}
export function createMarkdownToVueRenderFn(
srcDir: string,
siteConfig: SiteConfig,
options: MarkdownOptions = {},
pages: string[],
userDefines: Record<string, any> | undefined,
isBuild = false
) {
const md = createMarkdownRenderer(srcDir, options)
const { srcDir } = siteConfig;
const md = createMarkdownRenderer(siteConfig, options)
pages = pages.map((p) => slash(p.replace(/\.md$/, '')))
const userDefineRegex = userDefines

@ -80,7 +80,7 @@ export function createVitePressPlugin(
configResolved(resolvedConfig) {
config = resolvedConfig
markdownToVue = createMarkdownToVueRenderFn(
srcDir,
siteConfig,
markdown,
pages,
config.define,

3
types/shared.d.ts vendored

@ -40,7 +40,8 @@ export interface SiteData<ThemeConfig = any> {
*/
label: string
}
>
>,
cleanUrls: boolean;
}
export type HeadConfig =

Loading…
Cancel
Save