Skip to content

Commit

Permalink
feat: extend a theme
Browse files Browse the repository at this point in the history
  • Loading branch information
ulivz committed Oct 29, 2018
1 parent 1a0be05 commit 84fd0ff
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 17 deletions.
12 changes: 11 additions & 1 deletion packages/@vuepress/core/lib/internal-plugins/palette/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,22 @@ module.exports = (options, ctx) => ({
const themePaletteContent = fs.existsSync(themePalette)
? `@import(${JSON.stringify(themePalette)})`
: ''

const userPaletteContent = fs.existsSync(userPalette)
? `@import(${JSON.stringify(userPalette)})`
: ''

// user's palette can override theme's palette.
const paletteContent = themePaletteContent + userPaletteContent
let paletteContent = themePaletteContent + userPaletteContent

if (ctx.parentThemePath) {
const parentThemePalette = path.resolve(ctx.parentThemePath, 'styles/palette.styl')
const parentThemePaletteContent = fs.existsSync(parentThemePalette)
? `@import(${JSON.stringify(parentThemePalette)})`
: ''
paletteContent = parentThemePaletteContent + paletteContent
}

await writeTemp('palette.styl', paletteContent)
}
})
12 changes: 11 additions & 1 deletion packages/@vuepress/core/lib/internal-plugins/style/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,21 @@ module.exports = (options, ctx) => ({
const themeStyleContent = fs.existsSync(themeStyle)
? `@import(${JSON.stringify(themeStyle)})`
: ''

const userStyleContent = fs.existsSync(userStyle)
? `@import(${JSON.stringify(userStyle)})`
: ''

const styleContent = themeStyleContent + userStyleContent
let styleContent = themeStyleContent + userStyleContent

if (ctx.parentThemePath) {
const parentThemeStyle = path.resolve(ctx.parentThemePath, 'styles/index.styl')
const parentThemeStyleContent = fs.existsSync(parentThemeStyle)
? `@import(${JSON.stringify(parentThemeStyle)})`
: ''
styleContent = parentThemeStyleContent + styleContent
}

await writeTemp('style.styl', styleContent)
}
})
13 changes: 11 additions & 2 deletions packages/@vuepress/core/lib/prepare/AppContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ module.exports = class AppContext {
.use('@vuepress/register-components', {
componentsDir: [
path.resolve(this.sourceDir, '.vuepress/components'),
path.resolve(this.themePath, 'global-components')
path.resolve(this.themePath, 'global-components'),
this.parentThemePath && path.resolve(this.parentThemePath, 'global-components')
]
})
}
Expand All @@ -140,8 +141,11 @@ module.exports = class AppContext {
*/

applyUserPlugins () {
this.pluginAPI.useByPluginsConfig(this.cliOptions.plugins)
if (this.parentThemePath) {
this.pluginAPI.use(this.parentThemeEntryFile)
}
this.pluginAPI
.useByPluginsConfig(this.cliOptions.plugins)
.use(this.themeEntryFile)
.use(Object.assign({}, this.siteConfig, { name: '@vuepress/internal-site-config' }))
}
Expand Down Expand Up @@ -193,20 +197,25 @@ module.exports = class AppContext {
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
])

const devTemplate = fsExistsFallback([
siteDevTemplate,
siteDevTemplate2,
themeDevTemplate,
parentThemeDevTemplate,
defaultDevTemplate
])

Expand Down
62 changes: 52 additions & 10 deletions packages/@vuepress/core/lib/prepare/loadTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ module.exports = async function loadTheme (ctx) {
let themeEntryFile = null // Optional
let themeName
let themeShortcut
let parentThemePath = null // Optional
let parentThemeEntryFile = null // Optional

if (useLocalTheme) {
themePath = localThemePath
logger.tip(`\nApply theme located at ${chalk.gray(themePath)}...`)
} else if (isString(theme)) {
const resolved = themeResolver.resolve(theme, sourceDir)
const { entry: modulePath, name, shortcut } = resolved
if (modulePath === null) {
const { entry, name, shortcut } = resolved

if (entry === null) {
throw new Error(`Cannot resolve theme ${theme}.`)
}
if (modulePath.endsWith('.js') || modulePath.endsWith('.vue')) {
themePath = path.parse(modulePath).dir
} else {
themePath = modulePath
}

themePath = normalizeThemePath(resolved)
themeName = name
themeShortcut = shortcut
logger.tip(`\nApply theme ${chalk.gray(themeName)}`)
Expand All @@ -70,15 +70,43 @@ module.exports = async function loadTheme (ctx) {
} catch (error) {
themeEntryFile = {}
}

themeEntryFile.name = '@vuepress/internal-theme-entry-file'
themeEntryFile.shortcut = null

// handle theme api
const layoutDirs = [
path.resolve(themePath, 'layouts'),
path.resolve(themePath, '.')
path.resolve(themePath, '.'),
path.resolve(themePath, 'layouts')
]

if (themeEntryFile.extend) {
const resolved = themeResolver.resolve(themeEntryFile.extend, sourceDir)
if (resolved.entry === null) {
throw new Error(`Cannot resolve parent theme ${themeEntryFile.extend}.`)
}
parentThemePath = normalizeThemePath(resolved)

try {
parentThemeEntryFile = pluginAPI.normalizePlugin(parentThemePath, ctx.themeConfig)
} catch (error) {
parentThemeEntryFile = {}
}

parentThemeEntryFile.name = '@vuepress/internal-parent-theme-entry-file'
parentThemeEntryFile.shortcut = null

layoutDirs.unshift(
path.resolve(parentThemePath, '.'),
path.resolve(parentThemePath, 'layouts'),
)

themeEntryFile.alias = Object.assign(
themeEntryFile.alias || {},
{ '@parent-theme': parentThemePath }
)
}

// normalize component name
const getComponentName = filename => {
filename = filename.slice(0, -4)
Expand Down Expand Up @@ -139,6 +167,20 @@ module.exports = async function loadTheme (ctx) {
layoutComponentMap,
themeEntryFile,
themeName,
themeShortcut
themeShortcut,
parentThemePath,
parentThemeEntryFile
}
}

function normalizeThemePath (resolved) {
const { entry, name, fromDep } = resolved
if (fromDep) {
const pkgPath = require.resolve(`${name}/package.json`)
return path.parse(pkgPath).dir
} else if (entry.endsWith('.js') || entry.endsWith('.vue')) {
return path.parse(entry).dir
} else {
return entry
}
}
5 changes: 4 additions & 1 deletion packages/@vuepress/plugin-register-components/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { fs, path, globby } = require('@vuepress/shared-utils')
const { fs, path, globby, datatypes: { isString }} = require('@vuepress/shared-utils')

function fileToComponentName (file) {
return file
Expand Down Expand Up @@ -40,6 +40,9 @@ module.exports = (options, context) => ({

// 1. Register components in specified directories
for (const baseDir of baseDirs) {
if (!isString(baseDir)) {
continue
}
const files = await resolveComponents(baseDir) || []
code += files.map(file => genImport(baseDir, file)).join('\n') + '\n'
}
Expand Down
24 changes: 23 additions & 1 deletion packages/docs/docs/theme/option-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,26 @@ seoTitle: Option API | Theme
- Type: `Array|Object`
- Default: undefined

See: [Plugin > Using a plugin](../plugin/using-a-plugin.md).
**Also see:**

- [Plugin > Using a plugin](../plugin/using-a-plugin.md).

## extend

- Type: `String`
- Default: undefined

```js
module.exports = {
extend: '@vuepress/theme-default'
}
```

VuePress supports a theme to be inherited from another theme. VuePress will follow the principle of `override` to automatically help you resolve the priorities of various theme attributes, such as styles, layout components.

Note that in the child theme, VuePress will apply a `@parent-theme` [alias](../plugin/option-api.md#alias) pointing to the package directory of parent theme.

**Also see:**

- [Example: `@vuepress/theme-vue`](https://github.com/vuejs/vuepress/tree/master/packages/@vuepress/theme-vue)
- [Design Concepts of VuePress 1.x](../miscellaneous/design-concepts.md)
13 changes: 12 additions & 1 deletion packages/docs/docs/zh/theme/option-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,15 @@ seoTitle: Option API | Theme
- 类型: `Array|Object`
- 默认值: undefined

参考: [插件 > 使用插件](../plugin/using-a-plugin.md).
**参考:**

- [插件 > 使用插件](../plugin/using-a-plugin.md).

VuePress 支持一个主题继承于另一个主题。VuePress 将遵循 `override` 的方式自动帮你解决各种主题属性(如样式、布局组件)的优先级。

值得注意的是,在子主题中,VuePress 将注入一个指向父主题包目录根路径的 [alias](../plugin/option-api.md#alias) `@parent-theme`

**参考:**

- [例子: `@vuepress/theme-vue`](https://github.com/vuejs/vuepress/tree/master/packages/@vuepress/theme-vue)
- [Design Concepts of VuePress 1.x](../miscellaneous/design-concepts.md)

0 comments on commit 84fd0ff

Please sign in to comment.