Skip to content

Commit

Permalink
feat: refine theme api (#1319)
Browse files Browse the repository at this point in the history
  • Loading branch information
ulivz authored Feb 26, 2019
1 parent 40b3da8 commit d16d3d5
Show file tree
Hide file tree
Showing 47 changed files with 1,012 additions and 397 deletions.
45 changes: 0 additions & 45 deletions CHANGELOG.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,6 @@
### Features

* **$core:** support global layout (close: [#1226](https://github.com/vuejs/vuepress/issues/1226)) ([c91f55a](https://github.com/vuejs/vuepress/commit/c91f55a))

From now on, users have the ability to use a custom global layout component via [siteConfig](https://v1.vuepress.vuejs.org/miscellaneous/glossary.html#siteconfig) or [themeEntryFile](https://v1.vuepress.vuejs.org/miscellaneous/glossary.html#themeentryfile):

```js
module.exports = {
globalLayout: '/path/to/your/global/vue/sfc'
}
```

Here is the [content of default global layout component](https://github.com/vuejs/vuepress/blob/master/packages/%40vuepress/core/lib/app/components/GlobalLayout.vue), an example of setting global header and footer:

```vue
<template>
<div id="global-layout">
<header><h1>Header</h1></header>
<component :is="layout"/>
<footer><h1>Footer</h1></footer>
</div>
</template>
<script>
export default {
computed: {
layout () {
if (this.$page.path) {
if (this.$vuepress.isLayoutExists(this.$page.frontmatter.layout)) {
return this.$page.frontmatter.layout
}
return 'Layout'
}
return 'NotFound'
}
}
}
</script>
```

Also, you can follow the convention, directly create a component `.vuepress/components/GlobalLayout.vue` or `themePath/layouts/GlobalLayout.vue` without any config. the loading priority is as follows:

- siteConfig
- siteAgreement
- themeEntryFile
- themeAgreement
- default

* **$theme-default:** disable search box via frontmatter (close: [#1287](https://github.com/vuejs/vuepress/issues/1287)) ([#1288](https://github.com/vuejs/vuepress/issues/1288)) ([54e9eb0](https://github.com/vuejs/vuepress/commit/54e9eb0))


Expand Down
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions __mocks__/vuepress-theme-child/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extend: 'vuepress-theme-parent'
}
Empty file.
Empty file.
Empty file.
1 change: 1 addition & 0 deletions __mocks__/vuepress-theme-parent/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
Empty file.
Empty file.
27 changes: 27 additions & 0 deletions packages/@vuepress/core/__tests__/theme-api/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
jest.mock('vuepress-theme-parent')
jest.mock('vuepress-theme-child')

import ThemeAPI from '../../lib/theme-api'
import { resolve } from 'path'

const theme = {
path: resolve(process.cwd(), '__mocks__/vuepress-theme-child'),
name: 'vuepress-theme-child',
shortcut: 'child',
entryFile: require('vuepress-theme-child')
}

const parent = {
path: resolve(process.cwd(), '__mocks__/vuepress-theme-parent'),
name: 'vuepress-theme-parent',
shortcut: 'parent',
entryFile: {}
}

describe('ThemeAPI', () => {
test('extend', async () => {
const themeAPI = new ThemeAPI(theme, parent)
console.log(themeAPI.theme.entry)
})
// loadTheme('vuepress-theme-child')
})
14 changes: 8 additions & 6 deletions packages/@vuepress/core/lib/internal-plugins/enhanceApp.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ module.exports = (options, context) => ({
name: '@vuepress/internal-enhance-app',

enhanceAppFiles () {
const { sourceDir, themePath } = context
const { sourceDir, themeAPI } = context
const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js')
const themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js')
return [
enhanceAppPath,
themeEnhanceAppPath
]
const files = [enhanceAppPath]
if (themeAPI.existsParentTheme) {
files.push(path.resolve(themeAPI.parentTheme.path, 'enhanceApp.js'))
}
const themeEnhanceAppPath = path.resolve(themeAPI.theme.path, 'enhanceApp.js')
files.push(themeEnhanceAppPath)
return files
}
})
13 changes: 2 additions & 11 deletions packages/@vuepress/core/lib/internal-plugins/layoutComponents.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
module.exports = (options, ctx) => {
const { layoutComponentMap } = ctx
const componentNames = Object.keys(layoutComponentMap)

return {
name: '@vuepress/internal-layout-components',

async clientDynamicModules () {
const componentNames = Object.keys(ctx.themeAPI.layoutComponentMap)
const code = `export default {\n${componentNames
.map(name => ` ${JSON.stringify(name)}: () => import(${JSON.stringify(layoutComponentMap[name].path)})`)
.map(name => ` ${JSON.stringify(name)}: () => import(${JSON.stringify(ctx.themeAPI.layoutComponentMap[name].path)})`)
.join(',\n')} \n}`
return { name: 'layout-components.js', content: code, dirname: 'internal' }
},

chainWebpack (config, isServer) {
const setAlias = (alias, raw) => config.resolve.alias.set(alias, raw)
componentNames.forEach(name => {
setAlias(`@${name}`, layoutComponentMap[name].path)
})
}
}
}
6 changes: 3 additions & 3 deletions packages/@vuepress/core/lib/internal-plugins/palette/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = (options, ctx) => ({
// 2. write palette.styl
const { sourceDir, writeTemp } = ctx

const themePalette = path.resolve(ctx.themePath, 'styles/palette.styl')
const themePalette = path.resolve(ctx.themeAPI.theme.path, 'styles/palette.styl')
const userPalette = path.resolve(sourceDir, '.vuepress/styles/palette.styl')

const themePaletteContent = fs.existsSync(themePalette)
Expand All @@ -34,8 +34,8 @@ module.exports = (options, ctx) => ({
// user's palette can override theme's palette.
let paletteContent = themePaletteContent + userPaletteContent

if (ctx.parentThemePath) {
const parentThemePalette = path.resolve(ctx.parentThemePath, 'styles/palette.styl')
if (ctx.themeAPI.existsParentTheme) {
const parentThemePalette = path.resolve(ctx.themeAPI.parentTheme.path, 'styles/palette.styl')
const parentThemePaletteContent = fs.existsSync(parentThemePalette)
? `@import(${JSON.stringify(parentThemePalette.replace(/[\\]+/g, '/'))})`
: ''
Expand Down
12 changes: 8 additions & 4 deletions packages/@vuepress/core/lib/internal-plugins/style/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
const { fs, path, logger, chalk } = require('@vuepress/shared-utils')

/**
* @param options
* @param {AppContext} ctx
*/
module.exports = (options, ctx) => ({
name: '@vuepress/internal-style',

enhanceAppFiles: [path.resolve(__dirname, 'client.js')],

async ready () {
const { sourceDir, writeTemp } = ctx
const { sourceDir, writeTemp, themeAPI } = ctx

const overridePath = path.resolve(sourceDir, '.vuepress/override.styl')
const hasUserOverride = fs.existsSync(overridePath)
Expand All @@ -15,7 +19,7 @@ module.exports = (options, ctx) => ({
logger.tip(`${chalk.magenta('override.styl')} has been deprecated from v1.0.0, using ${chalk.cyan('.vuepress/styles/palette.styl')} instead.\n`)
}

const themeStyle = path.resolve(ctx.themePath, 'styles/index.styl')
const themeStyle = path.resolve(themeAPI.theme.path, 'styles/index.styl')
const userStyle = path.resolve(sourceDir, '.vuepress/styles/index.styl')

const themeStyleContent = fs.existsSync(themeStyle)
Expand All @@ -28,8 +32,8 @@ module.exports = (options, ctx) => ({

let styleContent = themeStyleContent + userStyleContent

if (ctx.parentThemePath) {
const parentThemeStyle = path.resolve(ctx.parentThemePath, 'styles/index.styl')
if (themeAPI.existsParentTheme) {
const parentThemeStyle = path.resolve(themeAPI.parentTheme.path, 'styles/index.styl')
const parentThemeStyleContent = fs.existsSync(parentThemeStyle)
? `@import(${JSON.stringify(parentThemeStyle.replace(/[\\]+/g, '/'))})`
: ''
Expand Down
9 changes: 6 additions & 3 deletions packages/@vuepress/core/lib/plugin-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ module.exports = class PluginAPI {
if (isPlainObject(pluginRaw) && pluginRaw.$$normalized) {
plugin = pluginRaw
} else {
plugin = this.normalizePlugin(pluginRaw, pluginOptions)
try {
plugin = this.normalizePlugin(pluginRaw, pluginOptions)
} catch (e) {
logger.warn(e.message)
}
}

if (plugin.multiple !== true) {
Expand Down Expand Up @@ -114,8 +118,7 @@ module.exports = class PluginAPI {
normalizePlugin (pluginRaw, pluginOptions = {}) {
let plugin = this._pluginResolver.resolve(pluginRaw)
if (!plugin.entry) {
console.warn(`[vuepress] cannot resolve plugin "${pluginRaw}"`)
return this
throw new Error(`[vuepress] cannot resolve plugin "${pluginRaw}"`)
}
plugin = flattenPlugin(plugin, pluginOptions, this._pluginContext, this)
plugin.$$normalized = true
Expand Down
94 changes: 34 additions & 60 deletions packages/@vuepress/core/lib/prepare/AppContext.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ module.exports = class AppContext {
this.resolveConfigAndInitialize()
this.resolveCacheLoaderOptions()
this.normalizeHeadTagUrls()
await this.resolveTheme()
this.themeAPI = loadTheme(this)
this.resolveTemplates()
this.resolveGlobalLayout()

Expand Down Expand Up @@ -137,7 +137,7 @@ module.exports = class AppContext {
)

this.pluginAPI
// internl core plugins
// internl core plugins
.use(require('../internal-plugins/siteData'))
.use(require('../internal-plugins/routes'))
.use(require('../internal-plugins/rootMixins'))
Expand All @@ -153,8 +153,8 @@ module.exports = class AppContext {
.use('@vuepress/register-components', {
componentsDir: [
path.resolve(this.sourceDir, '.vuepress/components'),
path.resolve(this.themePath, 'global-components'),
this.parentThemePath && path.resolve(this.parentThemePath, 'global-components')
path.resolve(this.themeAPI.theme.path, 'global-components'),
this.themeAPI.existsParentTheme && path.resolve(this.themeAPI.parentTheme.path, 'global-components')
]
})
}
Expand All @@ -167,11 +167,12 @@ module.exports = class AppContext {

applyUserPlugins () {
this.pluginAPI.useByPluginsConfig(this.cliOptions.plugins)
if (this.parentThemePath) {
this.pluginAPI.use(this.parentThemeEntryFile)
if (this.themeAPI.existsParentTheme) {
this.pluginAPI.use(this.themeAPI.parentTheme.entry)
}
this.pluginAPI
.use(this.themeEntryFile)
.use(this.themeAPI.theme.entry)
.use(this.themeAPI.vuepressPlugin)
.use(Object.assign({}, this.siteConfig, { name: '@vuepress/internal-site-config' }))
}

Expand Down Expand Up @@ -221,41 +222,26 @@ module.exports = class AppContext {
*/

resolveTemplates () {
const { siteSsrTemplate, siteDevTemplate } = this.siteConfig

const templateDir = path.resolve(this.vuepressDir, 'templates')
const siteSsrTemplate2 = path.resolve(templateDir, 'ssr.html')
const siteDevTemplate2 = path.resolve(templateDir, 'dev.html')

const themeSsrTemplate = path.resolve(this.themePath, 'templates/ssr.html')
const themeDevTemplate = path.resolve(this.themePath, 'templates/dev.html')

const parentThemeSsrTemplate = path.resolve(this.themePath, 'templates/ssr.html')
const parentThemeDevTemplate = path.resolve(this.themePath, 'templates/dev.html')

const defaultSsrTemplate = path.resolve(__dirname, '../app/index.ssr.html')
const defaultDevTemplate = path.resolve(__dirname, '../app/index.dev.html')

const ssrTemplate = fsExistsFallback([
siteSsrTemplate,
siteSsrTemplate2,
themeSsrTemplate,
parentThemeSsrTemplate,
defaultSsrTemplate
])
this.devTemplate = this.resolveCommonAgreementFilePath(
'devTemplate',
{
defaultValue: path.resolve(__dirname, '../app/index.dev.html'),
siteAgreement: 'templates/dev.html',
themeAgreement: 'templates/dev.html'
}
)

const devTemplate = fsExistsFallback([
siteDevTemplate,
siteDevTemplate2,
themeDevTemplate,
parentThemeDevTemplate,
defaultDevTemplate
])
this.ssrTemplate = this.resolveCommonAgreementFilePath(
'ssrTemplate',
{
defaultValue: path.resolve(__dirname, '../app/index.ssr.html'),
siteAgreement: 'templates/ssr.html',
themeAgreement: 'templates/ssr.html'
}
)

logger.debug('SSR Template File: ' + chalk.gray(ssrTemplate))
logger.debug('DEV Template File: ' + chalk.gray(devTemplate))
this.devTemplate = devTemplate
this.ssrTemplate = ssrTemplate
logger.debug('SSR Template File: ' + chalk.gray(this.ssrTemplate))
logger.debug('DEV Template File: ' + chalk.gray(this.devTemplate))
}

/**
Expand All @@ -266,14 +252,12 @@ module.exports = class AppContext {
*/

resolveGlobalLayout () {
const GLOBAL_LAYOUT_COMPONENT_NAME = `GlobalLayout`

this.globalLayout = this.resolveCommonAgreementFilePath(
'globalLayout',
{
defaultValue: path.resolve(__dirname, `../app/components/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`),
siteAgreement: `components/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`,
themeAgreement: `layouts/${GLOBAL_LAYOUT_COMPONENT_NAME}.vue`
defaultValue: path.resolve(__dirname, `../app/components/GlobalLayout.vue`),
siteAgreement: `components/GlobalLayout.vue`,
themeAgreement: `layouts/GlobalLayout.vue`
}
)

Expand Down Expand Up @@ -354,17 +338,6 @@ module.exports = class AppContext {
this.pages.push(page)
}

/**
* Resolve theme
*
* @returns {Promise<void>}
* @api private
*/

async resolveTheme () {
Object.assign(this, (await loadTheme(this)))
}

/**
* Get config value of current active theme.
*
Expand All @@ -374,7 +347,8 @@ module.exports = class AppContext {
*/

getThemeConfigValue (key) {
return this.themeEntryFile[key] || this.parentThemeEntryFile[key]
return this.themeAPI.theme.entry[key]
|| this.themeAPI.existsParentTheme && this.themeAPI.parentTheme.entry[key]
}

/**
Expand All @@ -386,12 +360,12 @@ module.exports = class AppContext {
*/

resolveThemeAgreementFile (filepath) {
const current = path.resolve(this.themePath, filepath)
const current = path.resolve(this.themeAPI.theme.path, filepath)
if (fs.existsSync(current)) {
return current
}
if (this.parentThemePath) {
const parent = path.resolve(this.parentThemePath, filepath)
if (this.themeAPI.existsParentTheme) {
const parent = path.resolve(this.themeAPI.theme.path, filepath)
if (fs.existsSync(parent)) {
return parent
}
Expand Down
Loading

0 comments on commit d16d3d5

Please sign in to comment.