Skip to content

Commit c91f55a

Browse files
committed
feat($core): support global layout (close: #1226)
1 parent fb13864 commit c91f55a

File tree

4 files changed

+101
-9
lines changed

4 files changed

+101
-9
lines changed

packages/@vuepress/core/lib/app/components/LayoutDistributor.vue packages/@vuepress/core/lib/app/components/GlobalLayout.vue

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33
</template>
44

55
<script>
6-
import { getLayoutAsyncComponent } from '../util'
7-
86
export default {
97
computed: {
108
layout () {
119
if (this.$page.path) {
12-
if (getLayoutAsyncComponent(this.$page.frontmatter.layout)) {
10+
if (this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)) {
1311
return this.$page.frontmatter.layout
1412
}
1513
return 'Layout'

packages/@vuepress/core/lib/app/plugins/VuePress.js

+17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
11
import Store from './Store'
2+
import {
3+
isPageExists,
4+
isPageLoaded,
5+
getPageAsyncComponent,
6+
isLayoutExists,
7+
isLayoutLoaded,
8+
getLayoutAsyncComponent
9+
} from '../util'
210

311
class VuePress extends Store {}
412

13+
Object.assign(VuePress.prototype, {
14+
isPageExists,
15+
isPageLoaded,
16+
getPageAsyncComponent,
17+
isLayoutExists,
18+
isLayoutLoaded,
19+
getLayoutAsyncComponent
20+
})
21+
522
export default {
623
install (Vue) {
724
const ins = new VuePress()

packages/@vuepress/core/lib/internal-plugins/routes.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@ module.exports = (options, ctx) => ({
33

44
// @internal/routes
55
async clientDynamicModules () {
6-
const code = importCode() + routesCode(ctx.pages)
6+
const code = importCode(ctx.globalLayout) + routesCode(ctx.pages)
77
return { name: 'routes.js', content: code, dirname: 'internal' }
88
}
99
})
1010

1111
/**
1212
* Import utilities
13+
* @param {string} globalLayout path of global layout component
1314
* @returns {string}
1415
*/
15-
function importCode () {
16+
function importCode (globalLayout) {
1617
return `
1718
import { injectComponentOption, ensureAsyncComponentsLoaded } from '@app/util'
1819
import rootMixins from '@internal/root-mixins'
19-
import LayoutDistributor from '@app/components/LayoutDistributor.vue'
20+
import GlobalLayout from ${JSON.stringify(globalLayout)}
2021
21-
injectComponentOption(LayoutDistributor, 'mixins', rootMixins)
22+
injectComponentOption(GlobalLayout, 'mixins', rootMixins)
2223
`
2324
}
2425

@@ -41,7 +42,7 @@ function routesCode (pages) {
4142
{
4243
name: ${JSON.stringify(componentName)},
4344
path: ${JSON.stringify(pagePath)},
44-
component: LayoutDistributor,
45+
component: GlobalLayout,
4546
beforeEnter: (to, from, next) => {
4647
ensureAsyncComponentsLoaded(${JSON.stringify(layout || 'Layout')}, ${JSON.stringify(componentName)}).then(next)
4748
},${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''}
@@ -78,7 +79,7 @@ function routesCode (pages) {
7879
const notFoundRoute = `,
7980
{
8081
path: '*',
81-
component: LayoutDistributor
82+
component: GlobalLayout
8283
}`
8384

8485
return (

packages/@vuepress/core/lib/prepare/AppContext.js

+76
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ module.exports = class AppContext {
9696
this.normalizeHeadTagUrls()
9797
await this.resolveTheme()
9898
this.resolveTemplates()
99+
this.resolveGlobalLayout()
99100

100101
this.applyInternalPlugins()
101102
this.applyUserPlugins()
@@ -257,6 +258,58 @@ module.exports = class AppContext {
257258
this.ssrTemplate = ssrTemplate
258259
}
259260

261+
/**
262+
* resolve global layout
263+
*
264+
* @returns {string}
265+
* @api private
266+
*/
267+
268+
resolveGlobalLayout () {
269+
const GLOBAL_LAYOUT_COMPONENT_NAME = `GlobalLayout`
270+
271+
this.globalLayout = this.resolveCommonAgreementFilePath(
272+
'globalLayout',
273+
{
274+
defaultValue: path.resolve(__dirname, `../app/components/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`),
275+
siteAgreement: `components/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`,
276+
themeAgreement: `layouts/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`
277+
}
278+
)
279+
280+
logger.debug('globalLayout: ' + chalk.gray(this.globalLayout))
281+
}
282+
283+
/**
284+
* Resolve a path-type config.
285+
*
286+
* @param {string} configKey
287+
* @param {string} defaultValue an absolute path
288+
* @param {string} siteAgreement a relative path to vuepress dir
289+
* @param {string} themeAgreement a relative path to theme dir
290+
* @returns {string | void}
291+
*/
292+
293+
resolveCommonAgreementFilePath (configKey, {
294+
defaultValue,
295+
siteAgreement,
296+
themeAgreement
297+
}) {
298+
const siteConfigValue = this.siteConfig[configKey]
299+
siteAgreement = path.resolve(this.vuepressDir, siteAgreement)
300+
301+
const themeConfigValue = this.getThemeConfigValue(configKey)
302+
themeAgreement = this.getThemeAgreementFile(themeAgreement)
303+
304+
return fsExistsFallback([
305+
siteConfigValue,
306+
siteAgreement,
307+
themeConfigValue,
308+
themeAgreement,
309+
defaultValue
310+
])
311+
}
312+
260313
/**
261314
* Find all page source files located in sourceDir
262315
*
@@ -312,6 +365,29 @@ module.exports = class AppContext {
312365
Object.assign(this, (await loadTheme(this)))
313366
}
314367

368+
/**
369+
* Get config value of current active theme.
370+
*
371+
* @param {string} key
372+
* @returns {any}
373+
* @api private
374+
*/
375+
376+
getThemeConfigValue (key) {
377+
return this.themeEntryFile[key] || this.parentThemeEntryFile[key]
378+
}
379+
380+
getThemeAgreementFile (filepath) {
381+
const current = path.resolve(this.themePath, filepath)
382+
if (fs.existsSync(current)) {
383+
return current
384+
}
385+
const parent = path.resolve(this.parentThemePath, filepath)
386+
if (fs.existsSync(parent)) {
387+
return parent
388+
}
389+
}
390+
315391
/**
316392
* Get the data to be delivered to the client.
317393
*

0 commit comments

Comments
 (0)