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

pull/488/head
Georges Gomes 4 years ago
parent 0c31bbac85
commit 3d83dea50d

@ -49,10 +49,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

@ -150,11 +150,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,

@ -63,6 +63,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<ThemeConfig = any> =
@ -82,6 +90,7 @@ export interface SiteConfig<ThemeConfig = any>
themeDir: string
outDir: string
tempDir: string
cleanUrls: boolean;
alias: AliasOptions
pages: string[]
}
@ -145,6 +154,7 @@ export async function resolveConfig(
configPath,
outDir,
tempDir: resolve(root, '.temp'),
cleanUrls: !!userConfig.cleanUrls,
markdown: userConfig.markdown,
lastUpdated: userConfig.lastUpdated,
alias: resolveAliases(root, themeDir),
@ -252,6 +262,7 @@ export async function resolveSiteData(
themeConfig: userConfig.themeConfig || {},
locales: userConfig.locales || {},
langs: createLangDictionary(userConfig),
scrollOffset: userConfig.scrollOffset || 90
scrollOffset: userConfig.scrollOffset || 90,
cleanUrls: userConfig.cleanUrls || false
}
}

@ -17,6 +17,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
@ -51,7 +52,8 @@ export type { Header }
export const createMarkdownRenderer = (
srcDir: string,
options: MarkdownOptions = {},
base: string
base: string,
cleanUrls: boolean = false,
): MarkdownRenderer => {
const md = MarkdownIt({
html: true,
@ -76,7 +78,7 @@ export const createMarkdownRenderer = (
rel: 'noopener noreferrer',
...options.externalLinks
},
base
base, cleanUrls
)
// 3rd party plugins
.use(attrs, options.attrs)

@ -13,6 +13,7 @@ export const linkPlugin = (
md: MarkdownIt,
externalAttrs: Record<string, string>,
base: string
shouldCleanUrls: boolean
) => {
md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
const token = tokens[idx]
@ -37,7 +38,7 @@ export const linkPlugin = (
// links to files (other than html/md)
!/\.(?!html|md)\w+($|\?)/i.test(url)
) {
normalizeHref(hrefAttr)
normalizeHref(hrefAttr, shouldCleanUrls)
}
// encode vite-specific replace strings in case they appear in URLs
@ -50,7 +51,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)
@ -58,13 +59,13 @@ export const linkPlugin = (
const [, path, hash] = indexMatch
url = path + hash
} else {
let cleanUrl = url.replace(/[?#].*$/, '')
// .md -> .html
let cleanUrl = url.replace(/[?#].*$/, '').replace(/\?.*$/, '')
// 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')

@ -28,9 +28,10 @@ export function createMarkdownToVueRenderFn(
userDefines: Record<string, any> | undefined,
isBuild = false,
base: string,
includeLastUpdatedData = false
includeLastUpdatedData = false,
cleanUrls: boolean = false
) {
const md = createMarkdownRenderer(srcDir, options, base)
const md = createMarkdownRenderer(srcDir, options, base, cleanUrls);
pages = pages.map((p) => slash(p.replace(/\.md$/, '')))
const userDefineRegex = userDefines

@ -43,7 +43,8 @@ export function createVitePressPlugin(
site,
vue: userVuePluginOptions,
vite: userViteConfig,
pages
pages,
cleanUrls
} = siteConfig
let markdownToVue: ReturnType<typeof createMarkdownToVueRenderFn>
@ -79,7 +80,8 @@ export function createVitePressPlugin(
config.define,
config.command === 'build',
config.base,
siteConfig.lastUpdated
siteConfig.lastUpdated,
cleanUrls
)
},

3
types/shared.d.ts vendored

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

Loading…
Cancel
Save