Skip to content

Commit 163f8a5

Browse files
committed
feat($blog): support category and tag
1 parent cb36ae6 commit 163f8a5

File tree

3 files changed

+232
-53
lines changed

3 files changed

+232
-53
lines changed

packages/@vuepress/core/lib/app/util.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ export function findPageForPath (pages, path) {
3535
}
3636
}
3737

38+
export function findPageByKey (pages, key) {
39+
for (let i = 0; i < pages.length; i++) {
40+
const page = pages[i]
41+
if (page.key === key) {
42+
return page
43+
}
44+
}
45+
return {
46+
path: '',
47+
frontmatter: {}
48+
}
49+
}
50+
3851
/**
3952
* Normalize config.
4053
* This utility is mainly for plugin developers. For some
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { findPageByKey } from '@app/util'
2+
import tagMeta from '@dynamic/tag'
3+
import categoryMeta from '@dynamic/category'
4+
5+
class Classifiable {
6+
constructor (metaMap, pages) {
7+
this._metaMap = Object.assign({}, metaMap)
8+
Object.keys(this._metaMap).forEach(name => {
9+
const { pageKeys } = this._metaMap[name]
10+
this._metaMap[name].posts = pageKeys.map(key => findPageByKey(pages, key))
11+
})
12+
}
13+
14+
get length () {
15+
return Object.keys(this._metaMap).length
16+
}
17+
18+
get map () {
19+
return this._metaMap
20+
}
21+
22+
get list () {
23+
return this.toArray()
24+
}
25+
26+
toArray () {
27+
const tags = []
28+
Object.keys(this._metaMap).forEach(name => {
29+
const { posts, path } = this._metaMap[name]
30+
tags.push({ name, posts, path })
31+
})
32+
return tags
33+
}
34+
35+
getItemByName (name) {
36+
return this._metaMap[name]
37+
}
38+
}
39+
40+
export default ({ Vue }) => {
41+
Vue.mixin({
42+
computed: {
43+
$tags () {
44+
const { pages } = this.$site
45+
const tags = new Classifiable(tagMeta, pages)
46+
return tags
47+
},
48+
$tag () {
49+
const tagName = this.$route.meta.tagName
50+
return this.$tags.getItemByName(tagName)
51+
},
52+
$categories () {
53+
const { pages } = this.$site
54+
const categories = new Classifiable(categoryMeta, pages)
55+
return categories
56+
},
57+
$category () {
58+
const categoryName = this.$route.meta.categoryName
59+
return this.$categories.getItemByName(categoryName)
60+
}
61+
}
62+
})
63+
}
Lines changed: 156 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,163 @@
11
const path = require('path')
2+
const { datatypes: { isString }} = require('@vuepress/shared-utils')
23

3-
module.exports = (options, ctx) => ({
4-
extendPageData (pageCtx) {
5-
const {
6-
frontmatter: rawFrontmatter
7-
// _filePath, // file's absolute path
8-
// _content, // file's raw content string
9-
// _localePath // '/' or '/zh/'
10-
// key, // file's hash key
11-
// regularPath // current page's default link (follow the file hierarchy)
12-
} = pageCtx
13-
14-
const { layoutComponentMap } = ctx
15-
const { pageEnhancers = [] } = options
16-
const isLayoutExists = name => layoutComponentMap[name] !== undefined
17-
const getLayout = (name, fallback) => isLayoutExists(name) ? name : fallback
18-
19-
const isDirectChild = regularPath => path.parse(regularPath).dir === '/'
20-
const enhancers = [
21-
{
22-
when: ({ regularPath }) => isDirectChild(regularPath),
23-
frontmatter: { layout: getLayout('Page', 'Layout') },
24-
data: { type: 'page' }
25-
},
26-
{
27-
when: ({ regularPath }) => regularPath === '/category.html',
28-
frontmatter: { layout: getLayout('Category', 'Page') }
29-
},
30-
{
31-
when: ({ regularPath }) => regularPath === '/tags.html',
32-
frontmatter: { layout: getLayout('Tag', 'Page') }
33-
},
34-
{
35-
when: ({ regularPath }) => regularPath === '/',
36-
frontmatter: { layout: getLayout('Layout') }
4+
module.exports = (options, ctx) => {
5+
const { layoutComponentMap } = ctx
6+
const {
7+
pageEnhancers = [],
8+
categoryIndexPageUrl = '/category/',
9+
tagIndexPageUrl = '/tag/'
10+
} = options
11+
12+
const isLayoutExists = name => layoutComponentMap[name] !== undefined
13+
const getLayout = (name, fallback) => isLayoutExists(name) ? name : fallback
14+
const isDirectChild = regularPath => path.parse(regularPath).dir === '/'
15+
16+
const enhancers = [
17+
{
18+
when: ({ regularPath }) => isDirectChild(regularPath),
19+
frontmatter: { layout: getLayout('Page', 'Layout') },
20+
data: { type: 'page' }
21+
},
22+
{
23+
when: ({ regularPath }) => regularPath.startsWith('/category/'),
24+
frontmatter: { layout: getLayout('Category', 'Page') }
25+
},
26+
{
27+
when: ({ regularPath }) => regularPath === categoryIndexPageUrl,
28+
frontmatter: { layout: getLayout('Categories', 'Page') }
29+
},
30+
{
31+
when: ({ regularPath }) => regularPath.startsWith('/tag/'),
32+
frontmatter: { layout: getLayout('Tag', 'Page') }
33+
},
34+
{
35+
when: ({ regularPath }) => regularPath === tagIndexPageUrl,
36+
frontmatter: { layout: getLayout('Tags', 'Page') }
37+
},
38+
{
39+
when: ({ regularPath }) => regularPath === '/',
40+
frontmatter: { layout: getLayout('Layout') }
41+
},
42+
{
43+
when: ({ regularPath }) => regularPath.startsWith('/_posts/'),
44+
frontmatter: {
45+
layout: getLayout('Post', 'Page'),
46+
permalink: '/:year/:month/:day/:slug'
3747
},
38-
{
39-
when: ({ regularPath }) => regularPath.startsWith('/_posts/'),
48+
data: { type: 'post' }
49+
},
50+
...pageEnhancers
51+
]
52+
53+
return {
54+
/**
55+
* Modify page's metadata according to the habits of blog users.
56+
*/
57+
extendPageData (pageCtx) {
58+
const { frontmatter: rawFrontmatter } = pageCtx
59+
60+
enhancers.forEach(({
61+
when,
62+
data = {},
63+
frontmatter = {}
64+
}) => {
65+
if (when(pageCtx)) {
66+
Object.assign(rawFrontmatter, frontmatter)
67+
Object.assign(pageCtx, data)
68+
}
69+
})
70+
},
71+
72+
/**
73+
* Create tag page and category page.
74+
*/
75+
ready () {
76+
const { pages } = ctx
77+
const tagMap = {}
78+
const categoryMap = {}
79+
80+
const curryHandler = (scope, map) => (key, pageKey) => {
81+
if (key) {
82+
if (!map[key]) {
83+
map[key] = {}
84+
map[key].path = `/${scope}/${key}.html`
85+
map[key].pageKeys = []
86+
}
87+
map[key].pageKeys.push(pageKey)
88+
}
89+
}
90+
91+
const handleTag = curryHandler('tag', tagMap)
92+
const handleCategory = curryHandler('category', categoryMap)
93+
94+
pages.forEach(({
95+
key,
4096
frontmatter: {
41-
layout: getLayout('Post', 'Page'),
42-
permalink: '/:year/:month/:day/:slug'
97+
tag,
98+
tags,
99+
category,
100+
categories
101+
}
102+
}) => {
103+
if (isString(tag)) {
104+
handleTag(tag, key)
105+
}
106+
if (Array.isArray(tags)) {
107+
tags.forEach(tag => handleTag(tag, key))
108+
}
109+
if (isString(category)) {
110+
handleCategory(categories, key)
111+
}
112+
if (Array.isArray(categories)) {
113+
categories.forEach(category => handleCategory(category, key))
114+
}
115+
})
116+
117+
ctx.tagMap = tagMap
118+
ctx.categoryMap = categoryMap
119+
120+
const extraPages = [
121+
{
122+
permalink: tagIndexPageUrl,
123+
frontmatter: { title: `Tags` }
43124
},
44-
data: { type: 'post' }
45-
},
46-
...pageEnhancers
47-
]
125+
{
126+
permalink: categoryIndexPageUrl,
127+
frontmatter: { title: `Categories` }
128+
},
129+
...Object.keys(tagMap).map(tagName => ({
130+
permalink: tagMap[tagName].path,
131+
meta: { tagName },
132+
frontmatter: { title: `${tagName} | Tag` }
133+
})),
134+
...Object.keys(categoryMap).map(categoryName => ({
135+
permalink: categoryMap[categoryName].path,
136+
meta: { categoryName },
137+
frontmatter: { title: `${categoryName} | Category` }
138+
}))
139+
]
140+
extraPages.forEach(page => ctx.addPage(page))
141+
},
48142

49-
enhancers.forEach(({
50-
when,
51-
data = {},
52-
frontmatter = {}
53-
}) => {
54-
if (when(pageCtx)) {
55-
Object.assign(rawFrontmatter, frontmatter)
56-
Object.assign(pageCtx, data)
57-
}
58-
})
143+
/**
144+
* Generate tag and category metadata.
145+
*/
146+
async clientDynamicModules () {
147+
return [
148+
{
149+
name: 'tag.js',
150+
content: `export default ${JSON.stringify(ctx.tagMap, null, 2)}`
151+
},
152+
{
153+
name: 'category.js',
154+
content: `export default ${JSON.stringify(ctx.categoryMap, null, 2)}`
155+
}
156+
]
157+
},
158+
159+
enhanceAppFiles: [
160+
path.resolve(__dirname, 'clientPlugin.js')
161+
]
59162
}
60-
})
163+
}

0 commit comments

Comments
 (0)