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

feat: refine theme api #1319

Merged
merged 11 commits into from
Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from 9 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
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.
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