From 3d83dea50d1c6dc710db70d4b6f9cb83a9203eee Mon Sep 17 00:00:00 2001 From: Georges Gomes Date: Tue, 28 Dec 2021 15:16:01 +0100 Subject: [PATCH] feat(cleanUrls): Generate "clean urls" in SPA and MPA mode --- src/client/app/router.ts | 13 +++++++++---- src/node/build/render.ts | 10 +++++++++- src/node/config.ts | 13 ++++++++++++- src/node/markdown/markdown.ts | 6 ++++-- src/node/markdown/plugins/link.ts | 15 ++++++++------- src/node/markdownToVue.ts | 5 +++-- src/node/plugin.ts | 6 ++++-- types/shared.d.ts | 3 ++- 8 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 678cf754..70fa1aa1 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -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 diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 88fef643..3aba38f2 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -150,11 +150,19 @@ export async function renderPage( ${inlinedScript} `.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, diff --git a/src/node/config.ts b/src/node/config.ts index a9277b85..3615b6b0 100644 --- a/src/node/config.ts +++ b/src/node/config.ts @@ -63,6 +63,14 @@ export interface UserConfig { * @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 = @@ -82,6 +90,7 @@ export interface SiteConfig 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 } } diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 46cbff35..faeb6288 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -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) diff --git a/src/node/markdown/plugins/link.ts b/src/node/markdown/plugins/link.ts index e88e425c..c2c4a431 100644 --- a/src/node/markdown/plugins/link.ts +++ b/src/node/markdown/plugins/link.ts @@ -13,6 +13,7 @@ export const linkPlugin = ( md: MarkdownIt, externalAttrs: Record, 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') diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index 22b9eb38..3eaced98 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -28,9 +28,10 @@ export function createMarkdownToVueRenderFn( userDefines: Record | 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 diff --git a/src/node/plugin.ts b/src/node/plugin.ts index 7f6e6f33..1e91c8e7 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -43,7 +43,8 @@ export function createVitePressPlugin( site, vue: userVuePluginOptions, vite: userViteConfig, - pages + pages, + cleanUrls } = siteConfig let markdownToVue: ReturnType @@ -79,7 +80,8 @@ export function createVitePressPlugin( config.define, config.command === 'build', config.base, - siteConfig.lastUpdated + siteConfig.lastUpdated, + cleanUrls ) }, diff --git a/types/shared.d.ts b/types/shared.d.ts index 645c0114..61348fc8 100644 --- a/types/shared.d.ts +++ b/types/shared.d.ts @@ -43,7 +43,8 @@ export interface SiteData { */ label: string } - > + >, + cleanUrls: boolean; } export type HeadConfig =