From f8672ed70c3bcbb9a4f9863bbc7282148a2ca4d1 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Tue, 5 Mar 2019 14:18:59 +0800 Subject: [PATCH 1/5] pascalize component name --- packages/@vuepress/core/lib/client/util.js | 14 ++++++++++---- .../lib/node/internal-plugins/layoutComponents.js | 4 +++- packages/@vuepress/shared-utils/src/index.ts | 2 ++ packages/@vuepress/shared-utils/src/pascalize.ts | 3 +++ 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 packages/@vuepress/shared-utils/src/pascalize.ts diff --git a/packages/@vuepress/core/lib/client/util.js b/packages/@vuepress/core/lib/client/util.js index e4e483f47b..e2f8eac200 100644 --- a/packages/@vuepress/core/lib/client/util.js +++ b/packages/@vuepress/core/lib/client/util.js @@ -4,6 +4,11 @@ import pageComponents from '@internal/page-components' const asyncComponents = Object.assign({}, layoutComponents, pageComponents) +// TODO: reuse this function in shared-utils +function pascalize (source = '') { + return source.replace(/(^|-)[a-z]/g, s => s.slice(-1).toUpperCase()) +} + export function isPageExists (pageKey) { return Boolean(pageComponents[pageKey]) } @@ -17,7 +22,7 @@ export function getPageAsyncComponent (pageKey) { } export function isLayoutExists (layout) { - return Boolean(layoutComponents[layout]) + return Boolean(getLayoutAsyncComponent(layout)) } export function isLayoutLoaded (layout) { @@ -25,11 +30,12 @@ export function isLayoutLoaded (layout) { } export function getLayoutAsyncComponent (pageKey) { - return layoutComponents[pageKey] + return layoutComponents[pascalize(pageKey)] } export function ensureAsyncComponentsLoaded (...names) { return Promise.all(names.filter(v => v).map(async (name) => { + name = pascalize(name) if (!Vue.component(name) && asyncComponents[name]) { const comp = await asyncComponents[name]() Vue.component(name, comp.default) @@ -96,7 +102,7 @@ export function findPageByKey (pages, key) { * * * Usage: - * + * ```js * import { normalizeConfig } from '@app/util' * export default { * data () { @@ -108,7 +114,7 @@ export function findPageByKey (pages, key) { * } * } * } - * + * ``` * * e.g. * diff --git a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js index d8b71a2417..a17c4daf21 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js @@ -1,3 +1,5 @@ +const { pascalize } = require('@vuepress/shared-utils') + module.exports = (options, ctx) => { return { name: '@vuepress/internal-layout-components', @@ -5,7 +7,7 @@ module.exports = (options, ctx) => { async clientDynamicModules () { const componentNames = Object.keys(ctx.themeAPI.layoutComponentMap) const code = `export default {\n${componentNames - .map(name => ` ${JSON.stringify(name)}: () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)})`) + .map(name => ` ${JSON.stringify(pascalize(name))}: () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)})`) .join(',\n')} \n}` return { name: 'layout-components.js', content: code, dirname: 'internal' } } diff --git a/packages/@vuepress/shared-utils/src/index.ts b/packages/@vuepress/shared-utils/src/index.ts index 689300274e..df7c1a4df6 100644 --- a/packages/@vuepress/shared-utils/src/index.ts +++ b/packages/@vuepress/shared-utils/src/index.ts @@ -18,6 +18,7 @@ import * as parseEmojis from './parseEmojis' import parseFrontmatter from './parseFrontmatter' import parseHeaders from './parseHeaders' import * as parseVueFrontmatter from './parseVueFrontmatter' +import pascalize from './pascalize' import performance from './performance' import slugify from './slugify' import sort from './sort' @@ -54,6 +55,7 @@ export { parseFrontmatter, parseHeaders, parseVueFrontmatter, + pascalize, performance, slugify, sort, diff --git a/packages/@vuepress/shared-utils/src/pascalize.ts b/packages/@vuepress/shared-utils/src/pascalize.ts new file mode 100644 index 0000000000..0c7a2f695c --- /dev/null +++ b/packages/@vuepress/shared-utils/src/pascalize.ts @@ -0,0 +1,3 @@ +export default function pascalize (source: string) { + return source.replace(/(^|-)[a-z]/g, s => s.slice(-1).toUpperCase()) +} From 61d1c8ea0ad82acd8c080dcce7918c1a52411811 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 6 Mar 2019 16:28:08 +0800 Subject: [PATCH 2/5] pascalize page key --- packages/@vuepress/core/lib/client/util.js | 4 ++-- .../core/lib/node/internal-plugins/pageComponents.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/@vuepress/core/lib/client/util.js b/packages/@vuepress/core/lib/client/util.js index e2f8eac200..f39c52f9f0 100644 --- a/packages/@vuepress/core/lib/client/util.js +++ b/packages/@vuepress/core/lib/client/util.js @@ -6,7 +6,7 @@ const asyncComponents = Object.assign({}, layoutComponents, pageComponents) // TODO: reuse this function in shared-utils function pascalize (source = '') { - return source.replace(/(^|-)[a-z]/g, s => s.slice(-1).toUpperCase()) + return source.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase()) } export function isPageExists (pageKey) { @@ -18,7 +18,7 @@ export function isPageLoaded (pageKey) { } export function getPageAsyncComponent (pageKey) { - return pageComponents[pageKey] + return pageComponents[pascalize(pageKey)] } export function isLayoutExists (layout) { diff --git a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js index e4f1377888..ce190db599 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js @@ -1,3 +1,5 @@ +const { pascalize } = require('@vuepress/shared-utils') + module.exports = (options, ctx) => { const { pages } = ctx // const componentNames = Object.keys(layoutComponentMap) @@ -8,7 +10,7 @@ module.exports = (options, ctx) => { async clientDynamicModules () { const code = `export default {\n${pages .filter(({ _filePath }) => _filePath) - .map(({ key, _filePath }) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(_filePath)})`) + .map(({ key, _filePath }) => ` ${JSON.stringify(pascalize(key))}: () => import(${JSON.stringify(_filePath)})`) .join(',\n')} \n}` return { name: 'page-components.js', content: code, dirname: 'internal' } } From 961b0ab7213c51e49d143625bdf86d9166bfd057 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 6 Mar 2019 16:41:54 +0800 Subject: [PATCH 3/5] fix --- packages/@vuepress/core/lib/client/util.js | 2 +- packages/@vuepress/shared-utils/src/pascalize.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@vuepress/core/lib/client/util.js b/packages/@vuepress/core/lib/client/util.js index f39c52f9f0..8a8f552e98 100644 --- a/packages/@vuepress/core/lib/client/util.js +++ b/packages/@vuepress/core/lib/client/util.js @@ -10,7 +10,7 @@ function pascalize (source = '') { } export function isPageExists (pageKey) { - return Boolean(pageComponents[pageKey]) + return Boolean(getPageAsyncComponent(pageKey)) } export function isPageLoaded (pageKey) { diff --git a/packages/@vuepress/shared-utils/src/pascalize.ts b/packages/@vuepress/shared-utils/src/pascalize.ts index 0c7a2f695c..21aa2caf5f 100644 --- a/packages/@vuepress/shared-utils/src/pascalize.ts +++ b/packages/@vuepress/shared-utils/src/pascalize.ts @@ -1,3 +1,3 @@ export default function pascalize (source: string) { - return source.replace(/(^|-)[a-z]/g, s => s.slice(-1).toUpperCase()) + return source.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase()) } From 4641f35ee95eb060c5e07b7c2ae550935bb94741 Mon Sep 17 00:00:00 2001 From: meteorlxy Date: Wed, 6 Mar 2019 18:40:42 +0800 Subject: [PATCH 4/5] refactor($core): auto register layouts and pages (for #1402) (#1409) --- packages/@vuepress/core/lib/client/app.js | 7 +-- .../core/lib/client/components/Content.js | 4 +- .../core/lib/client/plugins/VuePress.d.ts | 8 +--- .../core/lib/client/plugins/VuePress.js | 39 +++++++++------- packages/@vuepress/core/lib/client/util.js | 45 ------------------- .../node/internal-plugins/layoutComponents.js | 13 +++--- .../node/internal-plugins/pageComponents.js | 13 +++--- .../core/lib/node/internal-plugins/routes.js | 5 +-- 8 files changed, 42 insertions(+), 92 deletions(-) diff --git a/packages/@vuepress/core/lib/client/app.js b/packages/@vuepress/core/lib/client/app.js index bcccec1797..a98c4afb71 100644 --- a/packages/@vuepress/core/lib/client/app.js +++ b/packages/@vuepress/core/lib/client/app.js @@ -9,14 +9,12 @@ import globalUIComponents from '@internal/global-ui' import ClientComputedMixin from '@transform/ClientComputedMixin' import VuePress from './plugins/VuePress' import { handleRedirectForCleanUrls } from './redirect.js' -import { getLayoutAsyncComponent } from './util' // built-in components import Content from './components/Content.js' import ContentSlotsDistributor from './components/ContentSlotsDistributor' import OutboundLink from './components/OutboundLink.vue' import ClientOnly from './components/ClientOnly' -import TOC from './components/TOC.vue' // suggest dev server restart on base change if (module.hot) { @@ -44,11 +42,8 @@ Vue.component('ContentSlotsDistributor', ContentSlotsDistributor) Vue.component('OutboundLink', OutboundLink) // component for client-only content Vue.component('ClientOnly', ClientOnly) -// core components -Vue.component('Layout', getLayoutAsyncComponent('Layout')) -Vue.component('NotFound', getLayoutAsyncComponent('NotFound')) // markdown components -Vue.component('TOC', TOC) +Vue.component('TOC', () => import('./components/TOC.vue')) // global helper for adding base path to absolute urls Vue.prototype.$withBase = function (path) { diff --git a/packages/@vuepress/core/lib/client/components/Content.js b/packages/@vuepress/core/lib/client/components/Content.js index c2ed88d487..b4811acf72 100644 --- a/packages/@vuepress/core/lib/client/components/Content.js +++ b/packages/@vuepress/core/lib/client/components/Content.js @@ -1,5 +1,3 @@ -import { isPageExists } from '../util' - export default { props: { pageKey: String, @@ -10,7 +8,7 @@ export default { }, render (h) { const pageKey = this.pageKey || this.$parent.$page.key - if (isPageExists(pageKey)) { + if (this.$vuepress.isPageExists(pageKey)) { return h(pageKey) } return h('') diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts index ef4cf3782f..48af0be18c 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts @@ -4,13 +4,7 @@ import { AsyncComponent } from 'vue' declare class VuePress extends Store { isPageExists (pageKey: string): boolean; - isPageLoaded (pageKey: string): boolean; - - getPageAsyncComponent (pageKey: string): () => Promise; - - loadPageAsyncComponent (pageKey: string): Promise; - - registerPageAsyncComponent (pageKey: string): void; + isLayoutExists (pageKey: string): boolean; } declare module "vue/types/vue" { diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.js b/packages/@vuepress/core/lib/client/plugins/VuePress.js index bdb93e1b54..c5043b3138 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.js +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.js @@ -1,23 +1,28 @@ +import Vue from 'vue' import Store from './Store' -import { - isPageExists, - isPageLoaded, - getPageAsyncComponent, - isLayoutExists, - isLayoutLoaded, - getLayoutAsyncComponent -} from '../util' -class VuePress extends Store {} +// TODO: reuse this function in shared-utils +function cached (fn) { + const cache = Object.create(null) + return str => { + if (typeof cache[str] === 'undefined') { + cache[str] = fn(str) + } + return cache[str] + } +} -Object.assign(VuePress.prototype, { - isPageExists, - isPageLoaded, - getPageAsyncComponent, - isLayoutExists, - isLayoutLoaded, - getLayoutAsyncComponent -}) +const pascalize = cached((str = '') => str.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase())) + +class VuePress extends Store { + isPageExists (pageKey) { + return Boolean(Vue.component(pascalize(pageKey))) + } + + isLayoutExists (layout) { + return Boolean(Vue.component(pascalize(layout))) + } +} export default { install (Vue) { diff --git a/packages/@vuepress/core/lib/client/util.js b/packages/@vuepress/core/lib/client/util.js index 8a8f552e98..5094b3dc9e 100644 --- a/packages/@vuepress/core/lib/client/util.js +++ b/packages/@vuepress/core/lib/client/util.js @@ -1,48 +1,3 @@ -import Vue from 'vue' -import layoutComponents from '@internal/layout-components' -import pageComponents from '@internal/page-components' - -const asyncComponents = Object.assign({}, layoutComponents, pageComponents) - -// TODO: reuse this function in shared-utils -function pascalize (source = '') { - return source.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase()) -} - -export function isPageExists (pageKey) { - return Boolean(getPageAsyncComponent(pageKey)) -} - -export function isPageLoaded (pageKey) { - return Boolean(Vue.component(pageKey)) -} - -export function getPageAsyncComponent (pageKey) { - return pageComponents[pascalize(pageKey)] -} - -export function isLayoutExists (layout) { - return Boolean(getLayoutAsyncComponent(layout)) -} - -export function isLayoutLoaded (layout) { - return Boolean(Vue.component(layout)) -} - -export function getLayoutAsyncComponent (pageKey) { - return layoutComponents[pascalize(pageKey)] -} - -export function ensureAsyncComponentsLoaded (...names) { - return Promise.all(names.filter(v => v).map(async (name) => { - name = pascalize(name) - if (!Vue.component(name) && asyncComponents[name]) { - const comp = await asyncComponents[name]() - Vue.component(name, comp.default) - } - })) -} - /** * Inject option to Vue SFC * @param {object} options diff --git a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js index a17c4daf21..60ad57bcd2 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/layoutComponents.js @@ -4,12 +4,15 @@ module.exports = (options, ctx) => { return { name: '@vuepress/internal-layout-components', - async clientDynamicModules () { + async enhanceAppFiles () { const componentNames = Object.keys(ctx.themeAPI.layoutComponentMap) - const code = `export default {\n${componentNames - .map(name => ` ${JSON.stringify(pascalize(name))}: () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)})`) - .join(',\n')} \n}` - return { name: 'layout-components.js', content: code, dirname: 'internal' } + const code = `import Vue from 'vue'\n${componentNames + .map(name => `Vue.component(${JSON.stringify(pascalize(name))}, () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)}))`) + .join(',\n')} \n` + return { + name: 'layout-components.js', + content: code + } } } } diff --git a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js index ce190db599..022fc2fdc5 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/pageComponents.js @@ -7,12 +7,15 @@ module.exports = (options, ctx) => { return { name: '@vuepress/internal-page-components', - async clientDynamicModules () { - const code = `export default {\n${pages + async enhanceAppFiles () { + const code = `import Vue from 'vue'\n${pages .filter(({ _filePath }) => _filePath) - .map(({ key, _filePath }) => ` ${JSON.stringify(pascalize(key))}: () => import(${JSON.stringify(_filePath)})`) - .join(',\n')} \n}` - return { name: 'page-components.js', content: code, dirname: 'internal' } + .map(({ key, _filePath }) => `Vue.component(${JSON.stringify(pascalize(key))}, () => import(${JSON.stringify(_filePath)}))`) + .join(',\n')} \n` + return { + name: 'page-components.js', + content: code + } } } } diff --git a/packages/@vuepress/core/lib/node/internal-plugins/routes.js b/packages/@vuepress/core/lib/node/internal-plugins/routes.js index d6cf90b9b1..8c91601c00 100644 --- a/packages/@vuepress/core/lib/node/internal-plugins/routes.js +++ b/packages/@vuepress/core/lib/node/internal-plugins/routes.js @@ -42,10 +42,7 @@ function routesCode (pages) { { name: ${JSON.stringify(componentName)}, path: ${JSON.stringify(pagePath)}, - component: GlobalLayout, - beforeEnter: (to, from, next) => { - ensureAsyncComponentsLoaded(${JSON.stringify(layout || 'Layout')}, ${JSON.stringify(componentName)}).then(next) - },${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''} + component: GlobalLayout,${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''} }` const dncodedPath = decodeURIComponent(pagePath) From 65a27410d5e312a8f66dceea79d9eee265340079 Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Wed, 6 Mar 2019 19:23:52 +0800 Subject: [PATCH 5/5] refactor: integrate isLayoutExists and isPageExists --- .../core/lib/client/components/Content.js | 2 +- .../lib/client/components/GlobalLayout.vue | 2 +- .../core/lib/client/plugins/Store.d.ts | 12 +++++------- .../core/lib/client/plugins/VuePress.d.ts | 14 ++++---------- .../core/lib/client/plugins/VuePress.js | 18 ++++-------------- packages/docs/docs/theme/option-api.md | 2 +- packages/docs/docs/zh/theme/option-api.md | 2 +- 7 files changed, 17 insertions(+), 35 deletions(-) diff --git a/packages/@vuepress/core/lib/client/components/Content.js b/packages/@vuepress/core/lib/client/components/Content.js index b4811acf72..5e8bad5561 100644 --- a/packages/@vuepress/core/lib/client/components/Content.js +++ b/packages/@vuepress/core/lib/client/components/Content.js @@ -8,7 +8,7 @@ export default { }, render (h) { const pageKey = this.pageKey || this.$parent.$page.key - if (this.$vuepress.isPageExists(pageKey)) { + if (this.$hasComponent(pageKey)) { return h(pageKey) } return h('') diff --git a/packages/@vuepress/core/lib/client/components/GlobalLayout.vue b/packages/@vuepress/core/lib/client/components/GlobalLayout.vue index e137bb3c0f..bfcf5511d4 100644 --- a/packages/@vuepress/core/lib/client/components/GlobalLayout.vue +++ b/packages/@vuepress/core/lib/client/components/GlobalLayout.vue @@ -7,7 +7,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)) { + if (this.$hasComponent(this.$page.frontmatter.layout)) { return this.$page.frontmatter.layout } return 'Layout' diff --git a/packages/@vuepress/core/lib/client/plugins/Store.d.ts b/packages/@vuepress/core/lib/client/plugins/Store.d.ts index d290e852a7..007d0b6887 100644 --- a/packages/@vuepress/core/lib/client/plugins/Store.d.ts +++ b/packages/@vuepress/core/lib/client/plugins/Store.d.ts @@ -1,15 +1,13 @@ import Vue from 'Vue' export declare class Store { - store: Vue; + store: Vue - $get(key: string): any; + $get(key: string): any - $set(key: string, value: any): void; + $set(key: string, value: any): void - $emit: typeof Vue.prototype.$emit; + $emit: typeof Vue.prototype.$emit - $on: typeof Vue.prototype.$on; + $on: typeof Vue.prototype.$on } - - diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts index 48af0be18c..3d6eba736c 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.d.ts @@ -1,14 +1,8 @@ -import { Store } from './Store'; -import { AsyncComponent } from 'vue' +import { Store } from './Store' -declare class VuePress extends Store { - isPageExists (pageKey: string): boolean; - - isLayoutExists (pageKey: string): boolean; -} - -declare module "vue/types/vue" { +declare module 'vue/types/vue' { export interface Vue { - $vuepress: VuePress; + $vuepress: Store + $hasComponent (key: string): boolean } } diff --git a/packages/@vuepress/core/lib/client/plugins/VuePress.js b/packages/@vuepress/core/lib/client/plugins/VuePress.js index c5043b3138..afa90fa9be 100644 --- a/packages/@vuepress/core/lib/client/plugins/VuePress.js +++ b/packages/@vuepress/core/lib/client/plugins/VuePress.js @@ -1,4 +1,3 @@ -import Vue from 'vue' import Store from './Store' // TODO: reuse this function in shared-utils @@ -14,20 +13,11 @@ function cached (fn) { const pascalize = cached((str = '') => str.replace(/(^|-)\w/g, s => s.slice(-1).toUpperCase())) -class VuePress extends Store { - isPageExists (pageKey) { - return Boolean(Vue.component(pascalize(pageKey))) - } - - isLayoutExists (layout) { - return Boolean(Vue.component(pascalize(layout))) - } -} - export default { install (Vue) { - const ins = new VuePress() - Vue.$vuepress = ins - Vue.prototype.$vuepress = ins + const vuepress = new Store() + Vue.$vuepress = vuepress + Vue.prototype.$vuepress = vuepress + Vue.prototype.$hasComponent = key => Boolean(Vue.component(pascalize(key))) } } diff --git a/packages/docs/docs/theme/option-api.md b/packages/docs/docs/theme/option-api.md index 57ac02cd9c..0f8d7b0a25 100755 --- a/packages/docs/docs/theme/option-api.md +++ b/packages/docs/docs/theme/option-api.md @@ -111,7 +111,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) { + if (this.$hasComponent(this.$frontmatter.layout)) { return this.$frontmatter.layout } return 'Layout' diff --git a/packages/docs/docs/zh/theme/option-api.md b/packages/docs/docs/zh/theme/option-api.md index 3fb8ccd359..3a665eff34 100755 --- a/packages/docs/docs/zh/theme/option-api.md +++ b/packages/docs/docs/zh/theme/option-api.md @@ -111,7 +111,7 @@ export default { computed: { layout () { if (this.$page.path) { - if (this.$vuepress.isLayoutExists(this.$frontmatter.layout)) { + if (this.$hasComponent(this.$frontmatter.layout)) { return this.$frontmatter.layout } return 'Layout'