fix($core): PascalCase layouts cannot be used with camelCase nor hyphen-delimited (close: #1391)

This commit is contained in:
ULIVZ
2019-03-10 06:01:24 +08:00
parent 0306574b1f
commit 3e91eba5be
6 changed files with 88 additions and 39 deletions
@@ -1,5 +1,5 @@
import Vue from 'vue'
import { isPageExists, getPageAsyncComponent } from '../util'
import { getPageAsyncComponent } from '../util'
export default {
props: {
@@ -11,8 +11,9 @@ export default {
},
render (h) {
const pageKey = this.pageKey || this.$parent.$page.key
if (isPageExists(pageKey)) {
Vue.component(pageKey, getPageAsyncComponent(pageKey))
const pageComponent = getPageAsyncComponent(pageKey)
if (pageComponent) {
Vue.component(pageKey, pageComponent)
return h(pageKey)
}
return h('')
@@ -3,17 +3,14 @@
</template>
<script>
import Vue from 'vue'
export default {
computed: {
layout () {
if (this.$page.path) {
if (
this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)
|| Boolean(Vue.component(this.$page.frontmatter.layout))
) {
return this.$page.frontmatter.layout
const layout = this.$page.frontmatter.layout
if (layout && (this.$vuepress.getLayoutAsyncComponent(layout)
|| this.$vuepress.getVueComponent(layout))) {
return layout
}
return 'Layout'
}
@@ -1,22 +1,18 @@
import Store from './Store'
import {
isPageExists,
isPageLoaded,
getPageAsyncComponent,
isLayoutExists,
isLayoutLoaded,
getLayoutAsyncComponent
getLayoutAsyncComponent,
getAsyncComponent,
getVueComponent
} from '../util'
class VuePress extends Store {}
Object.assign(VuePress.prototype, {
isPageExists,
isPageLoaded,
getPageAsyncComponent,
isLayoutExists,
isLayoutLoaded,
getLayoutAsyncComponent
getLayoutAsyncComponent,
getAsyncComponent,
getVueComponent
})
export default {
+70 -17
View File
@@ -2,36 +2,89 @@ import Vue from 'vue'
import layoutComponents from '@internal/layout-components'
import pageComponents from '@internal/page-components'
/**
* Create a cached version of a pure function.
*/
function cached (fn) {
const cache = Object.create(null)
// eslint-disable-next-line func-names
return function cachedFn (str) {
const hit = cache[str]
// eslint-disable-next-line no-return-assign
return hit || (cache[str] = fn(str))
}
}
/**
* Camelize a hyphen-delimited string.
*/
const camelizeRE = /-(\w)/g
const camelize = cached(str => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
/**
* Hyphenate a camelCase string.
*/
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cached(str => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
/**
* Capitalize a string.
*/
const capitalize = cached(str => {
return str.charAt(0).toUpperCase() + str.slice(1)
})
/**
* This method was for securely getting Vue component when components
* are named in different style.
*
* e.g. a component named `a-b` can be also getted by `AB`, It's the
* same the other way round
*
* @param {function} getter a function of getting component by name
* @param {string} name component's name
* @returns {Component|AsyncComponent}
*/
export function getComponent (getter, name) {
if (!name) return
if (getter(name)) return getter(name)
const isKebabCase = name.includes('-')
if (isKebabCase) return getter(capitalize(camelize(name)))
return getter(capitalize(name)) || getter(hyphenate(name))
}
const asyncComponents = Object.assign({}, layoutComponents, pageComponents)
export function isPageExists (pageKey) {
return Boolean(pageComponents[pageKey])
}
export function isPageLoaded (pageKey) {
return Boolean(Vue.component(pageKey))
}
const asyncComponentsGetter = name => asyncComponents[name]
const pageComponentsGetter = layout => pageComponents[layout]
const layoutComponentsGetter = layout => layoutComponents[layout]
const globalComponentsGetter = name => Vue.component(name)
export function getPageAsyncComponent (pageKey) {
return pageComponents[pageKey]
return getComponent(pageComponentsGetter, pageKey)
}
export function isLayoutExists (layout) {
return Boolean(layoutComponents[layout])
export function getLayoutAsyncComponent (layout) {
return getComponent(layoutComponentsGetter, layout)
}
export function isLayoutLoaded (layout) {
return Boolean(Vue.component(layout))
export function getAsyncComponent (name) {
return getComponent(asyncComponentsGetter, name)
}
export function getLayoutAsyncComponent (pageKey) {
return layoutComponents[pageKey]
export function getVueComponent (name) {
return getComponent(globalComponentsGetter, name)
}
export function ensureAsyncComponentsLoaded (...names) {
return Promise.all(names.filter(v => v).map(async (name) => {
if (!Vue.component(name) && asyncComponents[name]) {
const comp = await asyncComponents[name]()
if (!getVueComponent(name) && getAsyncComponent(name)) {
const comp = await getAsyncComponent(name)()
Vue.component(name, comp.default)
}
}))
+2 -1
View File
@@ -111,7 +111,8 @@ export default {
computed: {
layout () {
if (this.$page.path) {
if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) {
if (this.$frontmatter.layout) {
// You can also check whether layout exists first as the default global layout does.
return this.$frontmatter.layout
}
return 'Layout'
+2 -1
View File
@@ -111,7 +111,8 @@ export default {
computed: {
layout () {
if (this.$page.path) {
if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) {
if (this.$frontmatter.layout) {
// 你也可以像默认的 globalLayout 一样首先检测 layout 是否存在
return this.$frontmatter.layout
}
return 'Layout'