Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Remove '.html' extension from URLs #1060

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
node_modules
*.log
.temp
.vscode
TODOs.md
packages/@vuepress/shared-utils/lib/
types
8 changes: 6 additions & 2 deletions packages/@vuepress/core/lib/prepare/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ module.exports = class AppContext {
this.cwd = process.cwd()

this.base = this.siteConfig.base || '/'
if (this.siteConfig.htmlSuffix === undefined) {
this.siteConfig.htmlSuffix = true
}
this.themeConfig = this.siteConfig.themeConfig || {}

const rawOutDir = this.cliOptions.dest || this.siteConfig.dest
Expand Down Expand Up @@ -425,7 +428,7 @@ module.exports = class AppContext {
*/

getSiteData () {
const { locales } = this.siteConfig
const { locales, htmlSuffix } = this.siteConfig
if (locales) {
Object.keys(locales).forEach(path => {
locales[path].path = path
Expand All @@ -438,7 +441,8 @@ module.exports = class AppContext {
base: this.base,
pages: this.pages.map(page => page.toJson()),
themeConfig: this.siteConfig.themeConfig || {},
locales
locales,
htmlSuffix
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@vuepress/core/lib/prepare/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ module.exports = class Page {
this._context = context

if (relative) {
this.regularPath = encodeURI(fileToPath(relative))
this.regularPath = encodeURI(fileToPath(relative, context.siteConfig.htmlSuffix))
} else if (path) {
this.regularPath = encodeURI(path)
} else if (permalink) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test('fileToPath', () => {
'foo/bar.md': '/foo/bar.html'
}
Object.keys(asserts).forEach(file => {
expect(fileToPath(file)).toBe(asserts[file])
expect(fileToPath(file, true)).toBe(asserts[file])
})
})

10 changes: 8 additions & 2 deletions packages/@vuepress/shared-utils/src/fileToPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ import { indexRE, isIndexFile } from './isIndexFile'

const extRE = /\.(vue|md)$/

export = function fileToPath (file: string): string {
export = function fileToPath (file: string, htmlSuffix: boolean) {
if (isIndexFile(file)) {
// README.md -> /
// README.vue -> /
// foo/README.md -> /foo/
// foo/README.vue -> /foo/
return file.replace(indexRE, '/$1')
} else {
} else if (htmlSuffix) {
// foo.md -> /foo.html
// foo.vue -> /foo.html
// foo/bar.md -> /foo/bar.html
// foo/bar.vue -> /foo/bar.html
return `/${file.replace(extRE, '').replace(/\\/g, '/')}.html`
} else {
// foo.md -> /foo/
// foo.vue -> /foo/
// foo/bar.md -> /foo/bar/
// foo/bar.vue -> /foo/bar/
return `/${file.replace(extRE, '').replace(/\\/g, '/')}/`
}
}
2 changes: 1 addition & 1 deletion packages/@vuepress/theme-default/components/NavLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default {

computed: {
link () {
return ensureExt(this.item.link)
return ensureExt(this.item.link, this.$site.htmlSuffix)
},

exact () {
Expand Down
12 changes: 6 additions & 6 deletions packages/@vuepress/theme-default/components/SidebarLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export default {
}) {
// use custom active class matching logic
// due to edge case of paths ending with / + hash
const selfActive = isActive($route, item.path)
const selfActive = isActive($route, item.path, $site.htmlSuffix)
// for sidebar: auto pages, a hash link should be active if one of its child
// matches
const active = item.type === 'auto'
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug, $site.htmlSuffix))
: selfActive
const link = renderLink(h, item.path, item.title || item.path, active)

Expand All @@ -41,10 +41,10 @@ export default {
|| $themeConfig.displayAllHeaders

if (item.type === 'auto') {
return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
return [link, renderChildren(h, item.children, item.basePath, $route, $site.htmlSuffix, maxDepth)]
} else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
const children = groupHeaders(item.headers)
return [link, renderChildren(h, children, item.path, $route, maxDepth)]
return [link, renderChildren(h, children, item.path, $route, $site.htmlSuffix, maxDepth)]
} else {
return link
}
Expand All @@ -65,10 +65,10 @@ function renderLink (h, to, text, active) {
}, text)
}

function renderChildren (h, children, path, route, maxDepth, depth = 1) {
function renderChildren (h, children, path, route, htmlSuffix, maxDepth, depth = 1) {
if (!children || depth > maxDepth) return null
return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
const active = isActive(route, path + '#' + c.slug)
const active = isActive(route, path + '#' + c.slug, htmlSuffix)
return h('li', { class: 'sidebar-sub-header' }, [
renderLink(h, path + '#' + c.slug, c.title, active),
renderChildren(h, c.children, path, route, maxDepth, depth + 1)
Expand Down
39 changes: 21 additions & 18 deletions packages/@vuepress/theme-default/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ export const extRE = /\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^(https?:|mailto:|tel:)/

export function normalize (path) {
return decodeURI(path)
export function normalize (path, htmlSuffix) {
const normalized = decodeURI(path)
.replace(hashRE, '')
.replace(extRE, '')

return htmlSuffix ? normalized : normalized.replace(endingSlashRE, '')
}

export function getHash (path) {
Expand All @@ -28,41 +30,42 @@ export function isTel (path) {
return /^tel:/.test(path)
}

export function ensureExt (path) {
export function ensureExt (path, htmlSuffix) {
if (isExternal(path)) {
return path
}
const hashMatch = path.match(hashRE)
const hash = hashMatch ? hashMatch[0] : ''
const normalized = normalize(path)
const normalized = normalize(path, htmlSuffix)
const ext = htmlSuffix ? '.html' : '/'

if (endingSlashRE.test(normalized)) {
return path
}
return normalized + '.html' + hash
return normalized + ext + hash
}

export function isActive (route, path) {
export function isActive (route, path, htmlSuffix) {
const routeHash = route.hash
const linkHash = getHash(path)
if (linkHash && routeHash !== linkHash) {
return false
}
const routePath = normalize(route.path)
const pagePath = normalize(path)
const routePath = normalize(route.path, htmlSuffix)
const pagePath = normalize(path, htmlSuffix)
return routePath === pagePath
}

export function resolvePage (pages, rawPath, base) {
export function resolvePage (pages, rawPath, base, htmlSuffix) {
if (base) {
rawPath = resolvePath(rawPath, base)
}
const path = normalize(rawPath)
const path = normalize(rawPath, htmlSuffix)
for (let i = 0; i < pages.length; i++) {
if (normalize(pages[i].regularPath) === path) {
if (normalize(pages[i].regularPath, htmlSuffix) === path) {
return Object.assign({}, pages[i], {
type: 'page',
path: ensureExt(pages[i].path)
path: ensureExt(pages[i].path, htmlSuffix)
})
}
}
Expand Down Expand Up @@ -116,7 +119,7 @@ function resolvePath (relative, base, append) {
* @returns { SidebarGroup }
*/
export function resolveSidebarItems (page, regularPath, site, localePath) {
const { pages, themeConfig } = site
const { pages, themeConfig, htmlSuffix } = site

const localeConfig = localePath && themeConfig.locales
? themeConfig.locales[localePath] || themeConfig
Expand All @@ -133,7 +136,7 @@ export function resolveSidebarItems (page, regularPath, site, localePath) {
} else {
const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig)
return config
? config.map(item => resolveItem(item, pages, base))
? config.map(item => resolveItem(item, pages, base, htmlSuffix))
: []
}
}
Expand Down Expand Up @@ -208,11 +211,11 @@ function ensureEndingSlash (path) {
: path + '/'
}

function resolveItem (item, pages, base, groupDepth = 1) {
function resolveItem (item, pages, base, htmlSuffix, groupDepth = 1) {
if (typeof item === 'string') {
return resolvePage(pages, item, base)
return resolvePage(pages, item, base, htmlSuffix)
} else if (Array.isArray(item)) {
return Object.assign(resolvePage(pages, item[0], base), {
return Object.assign(resolvePage(pages, item[0], base, htmlSuffix), {
title: item[1]
})
} else {
Expand All @@ -232,7 +235,7 @@ function resolveItem (item, pages, base, groupDepth = 1) {
path: item.path,
title: item.title,
sidebarDepth: item.sidebarDepth,
children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)),
children: children.map(child => resolveItem(child, pages, base, htmlSuffix, groupDepth + 1)),
collapsable: item.collapsable !== false
}
}
Expand Down