mirror of
https://github.com/wahyd4/vuepress.git
synced 2026-08-09 05:16:23 +10:00
feat: refine blog plugin
This commit is contained in:
@@ -291,6 +291,9 @@ module.exports = class AppContext {
|
||||
*/
|
||||
|
||||
async addPage (options) {
|
||||
if (this.pageExists(options)) {
|
||||
console.log(`Page already exists`, options)
|
||||
}
|
||||
options.permalinkPattern = this.siteConfig.permalink
|
||||
const page = new Page(options, this)
|
||||
await page.process({
|
||||
@@ -301,6 +304,10 @@ module.exports = class AppContext {
|
||||
this.pages.push(page)
|
||||
}
|
||||
|
||||
pageExists ({ permalink }) {
|
||||
return this.pages.includes(page => page.permalink === permalink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve theme
|
||||
*
|
||||
|
||||
@@ -45,7 +45,8 @@ module.exports = class Page {
|
||||
relative,
|
||||
permalink,
|
||||
frontmatter = {},
|
||||
permalinkPattern
|
||||
permalinkPattern,
|
||||
...rest
|
||||
}, context) {
|
||||
this.title = title
|
||||
this._meta = meta
|
||||
@@ -56,6 +57,8 @@ module.exports = class Page {
|
||||
this._permalinkPattern = permalinkPattern
|
||||
this._context = context
|
||||
|
||||
Object.assign(this, rest)
|
||||
|
||||
if (relative) {
|
||||
this.regularPath = encodeURI(fileToPath(relative))
|
||||
} else if (path) {
|
||||
|
||||
+23
-22
@@ -1,6 +1,5 @@
|
||||
import { findPageByKey } from '@app/util'
|
||||
import tagMeta from '@dynamic/tag'
|
||||
import categoryMeta from '@dynamic/category'
|
||||
import frontmatterClassifiedPageMap from '@dynamic/vuepress_blog/frontmatterClassified'
|
||||
|
||||
class Classifiable {
|
||||
constructor (metaMap, pages) {
|
||||
@@ -38,26 +37,28 @@ class Classifiable {
|
||||
}
|
||||
|
||||
export default ({ Vue }) => {
|
||||
Vue.mixin({
|
||||
computed: {
|
||||
$tags () {
|
||||
const { pages } = this.$site
|
||||
const tags = new Classifiable(tagMeta, pages)
|
||||
return tags
|
||||
},
|
||||
$tag () {
|
||||
const tagName = this.$route.meta.tagName
|
||||
return this.$tags.getItemByName(tagName)
|
||||
},
|
||||
$categories () {
|
||||
const { pages } = this.$site
|
||||
const categories = new Classifiable(categoryMeta, pages)
|
||||
return categories
|
||||
},
|
||||
$category () {
|
||||
const categoryName = this.$route.meta.categoryName
|
||||
return this.$categories.getItemByName(categoryName)
|
||||
const computed = Object.keys(frontmatterClassifiedPageMap)
|
||||
.map(classifiedType => {
|
||||
const map = frontmatterClassifiedPageMap[classifiedType]
|
||||
const helperName = `$${classifiedType}`
|
||||
return {
|
||||
[helperName] () {
|
||||
const { pages } = this.$site
|
||||
const classified = new Classifiable(map, pages)
|
||||
return classified
|
||||
},
|
||||
[`$current${classifiedType.charAt(0).toUpperCase() + classifiedType.slice(1)}`] () {
|
||||
const tagName = this.$route.meta.frontmatterClassificationKey
|
||||
return this[helperName].getItemByName(tagName)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.reduce((map, item) => {
|
||||
Object.assign(map, item)
|
||||
return map
|
||||
}, {})
|
||||
|
||||
Vue.mixin({
|
||||
computed
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import Vue from 'vue'
|
||||
import paginations from '@dynamic/vuepress_blog/paginations'
|
||||
import frontmatterClassifications from '@dynamic/vuepress_blog/frontmatterClassifications'
|
||||
import pageFilters from '@dynamic/vuepress_blog/pageFilters'
|
||||
import pageSorters from '@dynamic/vuepress_blog/pageSorters'
|
||||
import _debug from 'debug'
|
||||
|
||||
const debug = _debug('plugin-blog:pagination')
|
||||
|
||||
function getClientFrontmatterPageFilter (rawFilter, pid, value) {
|
||||
// debug('getClientFrontmatterPageFilter')
|
||||
// debug('frontmatterClassifications', frontmatterClassifications)
|
||||
// debug('pid', pid)
|
||||
const match = frontmatterClassifications.filter(i => i.id === pid)[0]
|
||||
return page => rawFilter(page, match && match.keys, value)
|
||||
}
|
||||
|
||||
class PaginationGateway {
|
||||
constructor (paginations) {
|
||||
this.paginations = paginations
|
||||
}
|
||||
|
||||
get pages () {
|
||||
return Vue.$vuepress.$get('siteData').pages
|
||||
}
|
||||
|
||||
getPagination (pid, id, route) {
|
||||
debug('id', id)
|
||||
debug('this.paginations', this.paginations)
|
||||
const pagnination = this.paginations.filter(p => p.id === id && p.pid === pid)[0]
|
||||
return new Pagination(pagnination, this.pages, route)
|
||||
}
|
||||
}
|
||||
|
||||
const gateway = new PaginationGateway(paginations)
|
||||
|
||||
class Pagination {
|
||||
constructor (pagination, pages, route) {
|
||||
debug(pagination)
|
||||
const { pid, id, paginationPages } = pagination
|
||||
|
||||
const pageFilter = getClientFrontmatterPageFilter(pageFilters[pid], pid, id)
|
||||
const pageSorter = pageSorters[pid]
|
||||
|
||||
const { path } = route
|
||||
|
||||
for (let i = 0, l = paginationPages.length; i < l; i++) {
|
||||
const page = paginationPages[i]
|
||||
if (page.path === path) {
|
||||
this.paginationIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.paginationIndex) {
|
||||
this.paginationIndex = 0
|
||||
}
|
||||
|
||||
this._paginationPages = paginationPages
|
||||
this._currentPage = paginationPages[this.paginationIndex]
|
||||
this._matchedPages = pages.filter(pageFilter).sort(pageSorter)
|
||||
}
|
||||
|
||||
setIndexPage (path) {
|
||||
this._indexPage = path
|
||||
}
|
||||
|
||||
get length () {
|
||||
return this._paginationPages.length
|
||||
}
|
||||
|
||||
get pages () {
|
||||
const [start, end] = this._currentPage.interval
|
||||
return this._matchedPages.slice(start, end + 1)
|
||||
}
|
||||
|
||||
get hasPrev () {
|
||||
return this.paginationIndex !== 0
|
||||
}
|
||||
|
||||
get prevLink () {
|
||||
if (this.hasPrev) {
|
||||
if (this.paginationIndex - 1 === 0 && this._indexPage) {
|
||||
return this._indexPage
|
||||
}
|
||||
return this._paginationPages[this.paginationIndex - 1].path
|
||||
}
|
||||
}
|
||||
|
||||
get hasNext () {
|
||||
return this.paginationIndex !== this.length - 1
|
||||
}
|
||||
|
||||
get nextLink () {
|
||||
if (this.hasNext) {
|
||||
return this._paginationPages[this.paginationIndex + 1].path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ({ Vue }) => {
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
$pagination (pid, id) {
|
||||
id = id || pid
|
||||
return gateway.getPagination(pid, id, this.$route)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
export interface Pagination {
|
||||
postsFilter?: typeof Array.prototype.filter
|
||||
postsSorter?: typeof Array.prototype.sort
|
||||
perPagePosts?: number
|
||||
layout?: string
|
||||
}
|
||||
|
||||
export interface DirectoryClassification {
|
||||
id: string;
|
||||
dirname: string;
|
||||
path: string;
|
||||
layout?: string;
|
||||
itemLayout?: string;
|
||||
itemPermalink?: string;
|
||||
pagination?: Pagination;
|
||||
}
|
||||
|
||||
export interface FrontmatterClassification {
|
||||
id: string;
|
||||
keys: string[];
|
||||
path: string;
|
||||
layout?: string;
|
||||
frontmatter?: Record<string, any>;
|
||||
itemlayout?: string;
|
||||
pagination?: Pagination;
|
||||
}
|
||||
@@ -1,81 +1,23 @@
|
||||
const { path, datatypes: { isString }} = require('@vuepress/shared-utils')
|
||||
const { path } = require('@vuepress/shared-utils')
|
||||
const { curryFrontmatterHandler } = require('./lib/util')
|
||||
const { handleOptions } = require('./lib/handleOptions')
|
||||
const { registerPagination } = require('./lib/pagination')
|
||||
|
||||
module.exports = (options, ctx) => {
|
||||
const { layoutComponentMap } = ctx
|
||||
const {
|
||||
pageEnhancers = [],
|
||||
postsDir = '_posts',
|
||||
archivePostsDir = '_archive',
|
||||
archivePageUrl = '/archive/',
|
||||
categoryIndexPageUrl = '/category/',
|
||||
tagIndexPageUrl = '/tag/',
|
||||
permalink = '/:year/:month/:day/:slug'
|
||||
} = options
|
||||
|
||||
const isLayoutExists = name => layoutComponentMap[name] !== undefined
|
||||
const getLayout = (name, fallback) => {
|
||||
return isLayoutExists(name) ? name : (fallback || 'Layout')
|
||||
}
|
||||
|
||||
const isDirectChild = regularPath => path.parse(regularPath).dir === '/'
|
||||
|
||||
const enhancers = [
|
||||
{
|
||||
when: ({ regularPath }) => regularPath === categoryIndexPageUrl,
|
||||
frontmatter: { layout: getLayout('Categories', 'Page') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath.startsWith('/category/'),
|
||||
frontmatter: { layout: getLayout('Category', 'Page') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath === tagIndexPageUrl,
|
||||
frontmatter: { layout: getLayout('Tags', 'Page') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath.startsWith('/tag/'),
|
||||
frontmatter: { layout: getLayout('Tag', 'Page') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath === '/',
|
||||
frontmatter: { layout: getLayout('Layout') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath.startsWith(`/${postsDir}/`),
|
||||
frontmatter: {
|
||||
layout: getLayout('Post', 'Page'),
|
||||
permalink
|
||||
},
|
||||
data: { type: 'post' }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath === archivePageUrl,
|
||||
frontmatter: { layout: getLayout('Archive', 'Page') }
|
||||
},
|
||||
{
|
||||
when: ({ regularPath }) => regularPath.startsWith(`/${archivePostsDir}/`),
|
||||
frontmatter: {
|
||||
layout: getLayout('Post', 'Page'),
|
||||
permalink: `/archive/${permalink}`
|
||||
},
|
||||
data: { type: 'archived_post' }
|
||||
},
|
||||
...pageEnhancers,
|
||||
{
|
||||
when: ({ regularPath }) => isDirectChild(regularPath),
|
||||
frontmatter: { layout: getLayout('Page', 'Layout') },
|
||||
data: { type: 'page' }
|
||||
}
|
||||
]
|
||||
pageEnhancers,
|
||||
frontmatterClassificationPages,
|
||||
extraPages,
|
||||
paginations
|
||||
} = handleOptions(options, ctx)
|
||||
|
||||
return {
|
||||
/**
|
||||
* Modify page's metadata according to the habits of blog users.
|
||||
* Modify page's metadata.
|
||||
*/
|
||||
extendPageData (pageCtx) {
|
||||
const { frontmatter: rawFrontmatter } = pageCtx
|
||||
|
||||
enhancers.forEach(({
|
||||
pageEnhancers.forEach(({
|
||||
when,
|
||||
data = {},
|
||||
frontmatter = {}
|
||||
@@ -97,92 +39,150 @@ module.exports = (options, ctx) => {
|
||||
*/
|
||||
async ready () {
|
||||
const { pages } = ctx
|
||||
const tagMap = {}
|
||||
const categoryMap = {}
|
||||
|
||||
const curryHandler = (scope, map) => (key, pageKey) => {
|
||||
if (key) {
|
||||
if (!map[key]) {
|
||||
map[key] = {}
|
||||
map[key].path = `/${scope}/${key}.html`
|
||||
map[key].pageKeys = []
|
||||
}
|
||||
map[key].pageKeys.push(pageKey)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTag = curryHandler('tag', tagMap)
|
||||
const handleCategory = curryHandler('category', categoryMap)
|
||||
|
||||
pages.forEach(({
|
||||
key,
|
||||
frontmatter: {
|
||||
tag,
|
||||
tags,
|
||||
category,
|
||||
categories
|
||||
}
|
||||
// 1. Tweak data structure to store the classified info.
|
||||
const frontmatterClassifiedPages = frontmatterClassificationPages.map(({
|
||||
id,
|
||||
keys,
|
||||
pagination
|
||||
}) => {
|
||||
if (isString(tag)) {
|
||||
handleTag(tag, key)
|
||||
}
|
||||
if (Array.isArray(tags)) {
|
||||
tags.forEach(tag => handleTag(tag, key))
|
||||
}
|
||||
if (isString(category)) {
|
||||
handleCategory(category, key)
|
||||
}
|
||||
if (Array.isArray(categories)) {
|
||||
categories.forEach(category => handleCategory(category, key))
|
||||
const map = {}
|
||||
return {
|
||||
id,
|
||||
keys,
|
||||
pagination,
|
||||
map,
|
||||
_handler: curryFrontmatterHandler(id, map)
|
||||
}
|
||||
})
|
||||
|
||||
ctx.tagMap = tagMap
|
||||
ctx.categoryMap = categoryMap
|
||||
// 2. Handle frontmatter per page.
|
||||
for (const { key: pageKey, frontmatter } of pages) {
|
||||
if (!frontmatter || Object.keys(frontmatter).length === 0) {
|
||||
continue
|
||||
}
|
||||
for (const { keys, _handler } of frontmatterClassifiedPages) {
|
||||
for (const key of keys) {
|
||||
const fieldValue = frontmatter[key]
|
||||
Array.isArray(fieldValue)
|
||||
? fieldValue.forEach(v => _handler(v, pageKey))
|
||||
: _handler(fieldValue, pageKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const extraPages = [
|
||||
{
|
||||
permalink: tagIndexPageUrl,
|
||||
frontmatter: { title: `Tags` }
|
||||
},
|
||||
{
|
||||
permalink: categoryIndexPageUrl,
|
||||
frontmatter: { title: `Categories` }
|
||||
},
|
||||
{
|
||||
permalink: archivePageUrl,
|
||||
frontmatter: { title: `Archive` }
|
||||
},
|
||||
...Object.keys(tagMap).map(tagName => ({
|
||||
permalink: tagMap[tagName].path,
|
||||
meta: { tagName },
|
||||
frontmatter: { title: `${tagName} | Tag` }
|
||||
})),
|
||||
...Object.keys(categoryMap).map(categoryName => ({
|
||||
permalink: categoryMap[categoryName].path,
|
||||
meta: { categoryName },
|
||||
frontmatter: { title: `${categoryName} | Category` }
|
||||
}))
|
||||
ctx.frontmatterClassifiedPages = frontmatterClassifiedPages
|
||||
|
||||
// 3. Staticize all pages.
|
||||
const allExtraPages = [
|
||||
...extraPages,
|
||||
...frontmatterClassifiedPages
|
||||
.map(frontmatterClassifiedPage => {
|
||||
const { map, pagination, keys } = frontmatterClassifiedPage
|
||||
return Object.keys(map).map((key) => {
|
||||
const { path, scope } = map[key]
|
||||
|
||||
// Register pagination
|
||||
paginations.push({
|
||||
pid: scope,
|
||||
id: key,
|
||||
options: {
|
||||
...pagination,
|
||||
layout: 'FrontmatterClassification',
|
||||
serverPageFilter (page) {
|
||||
return clientFrontmatterClassificationPageFilter(page, keys, key)
|
||||
},
|
||||
clientPageFilter: clientFrontmatterClassificationPageFilter
|
||||
},
|
||||
getUrl (index) {
|
||||
if (index === 0) {
|
||||
return `/${scope}/${key}/`
|
||||
}
|
||||
return `/${scope}/${key}/page/${index + 1}/`
|
||||
},
|
||||
getTitle (index) {
|
||||
return `Page ${index + 1} - ${key} | ${scope}`
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
permalink: path,
|
||||
meta: {
|
||||
frontmatterClassificationKey: scope
|
||||
},
|
||||
pid: scope,
|
||||
id: key,
|
||||
frontmatter: {
|
||||
layout: 'FrontmatterClassification',
|
||||
title: `${key} | ${scope}`
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.reduce((arr, next) => arr.concat(next), [])
|
||||
]
|
||||
await Promise.all(extraPages.map(page => ctx.addPage(page)))
|
||||
|
||||
await Promise.all(allExtraPages.map(async page => ctx.addPage(page)))
|
||||
await registerPagination(paginations, ctx)
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate tag and category metadata.
|
||||
*/
|
||||
async clientDynamicModules () {
|
||||
const frontmatterClassifiedPageMap = ctx.frontmatterClassifiedPages
|
||||
.reduce((map, page) => {
|
||||
map[page.id] = page.map
|
||||
return map
|
||||
}, {})
|
||||
|
||||
const PREFIX = 'vuepress_blog'
|
||||
return [
|
||||
{
|
||||
name: 'tag.js',
|
||||
content: `export default ${JSON.stringify(ctx.tagMap, null, 2)}`
|
||||
name: `${PREFIX}/frontmatterClassifications.js`,
|
||||
content: `export default ${JSON.stringify(frontmatterClassificationPages, null, 2)}`
|
||||
},
|
||||
{
|
||||
name: 'category.js',
|
||||
content: `export default ${JSON.stringify(ctx.categoryMap, null, 2)}`
|
||||
name: `${PREFIX}/frontmatterClassified.js`,
|
||||
content: `export default ${JSON.stringify(frontmatterClassifiedPageMap, null, 2)}`
|
||||
},
|
||||
{
|
||||
name: `${PREFIX}/paginations.js`,
|
||||
content: `export default ${JSON.stringify(ctx.paginations, null, 2)}`
|
||||
},
|
||||
{
|
||||
name: `${PREFIX}/pageFilters.js`,
|
||||
content: `export default ${mapToString(ctx.pageFilters)}`
|
||||
},
|
||||
{
|
||||
name: `${PREFIX}/pageSorters.js`,
|
||||
content: `export default ${mapToString(ctx.pageSorters)}`
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
enhanceAppFiles: path.resolve(__dirname, 'enhanceAppFile.js')
|
||||
enhanceAppFiles: [
|
||||
path.resolve(__dirname, 'client/classification.js'),
|
||||
path.resolve(__dirname, 'client/pagination.js')
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function mapToString (map) {
|
||||
let str = `{\n`
|
||||
for (const key of Object.keys(map)) {
|
||||
str += ` "${key}": ${map[key]},\n`
|
||||
}
|
||||
str += '}'
|
||||
return str
|
||||
}
|
||||
|
||||
function clientFrontmatterClassificationPageFilter (page, keys, value) {
|
||||
return keys.some(key => {
|
||||
const _value = page.frontmatter[key]
|
||||
if (Array.isArray(_value)) {
|
||||
return _value.some(i => i === value)
|
||||
}
|
||||
return _value === value
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
function handleOptions (options, ctx) {
|
||||
const { layoutComponentMap } = ctx
|
||||
const {
|
||||
directories = [],
|
||||
frontmatters = []
|
||||
} = options
|
||||
|
||||
const isLayoutExists = name => layoutComponentMap[name] !== undefined
|
||||
const getLayout = (name, fallback) => {
|
||||
return isLayoutExists(name) ? name : (fallback || 'Layout')
|
||||
}
|
||||
|
||||
const pageEnhancers = []
|
||||
const frontmatterClassificationPages = []
|
||||
const extraPages = []
|
||||
const paginations = []
|
||||
|
||||
// 1. Directory-based classification
|
||||
for (const directory of directories) {
|
||||
const {
|
||||
id,
|
||||
dirname,
|
||||
path: listPath,
|
||||
layout: listLayout,
|
||||
frontmatter,
|
||||
itemLayout,
|
||||
itemPermalink,
|
||||
pagination = {
|
||||
perPagePosts: 10
|
||||
}
|
||||
} = directory
|
||||
|
||||
if (!listPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
extraPages.push({
|
||||
permalink: listPath,
|
||||
frontmatter
|
||||
})
|
||||
|
||||
pageEnhancers.push({
|
||||
when: ({ regularPath }) => regularPath === listPath,
|
||||
frontmatter: { layout: getLayout(listLayout) }
|
||||
})
|
||||
|
||||
pageEnhancers.push({
|
||||
when: ({ regularPath }) => regularPath && regularPath !== listPath && regularPath.startsWith(`/${dirname}/`),
|
||||
frontmatter: {
|
||||
layout: getLayout(itemLayout),
|
||||
permalink: itemPermalink
|
||||
},
|
||||
data: { id: id, pid: id }
|
||||
})
|
||||
|
||||
paginations.push({
|
||||
pid: id,
|
||||
id,
|
||||
options: pagination,
|
||||
getUrl (index) {
|
||||
if (index === 0) {
|
||||
return listPath
|
||||
}
|
||||
return `${listPath}page/${index + 1}/`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 2. Frontmatter-based classification
|
||||
for (const frontmatterPage of frontmatters) {
|
||||
const {
|
||||
id,
|
||||
keys,
|
||||
path: listPath,
|
||||
layout: listLayout,
|
||||
frontmatter,
|
||||
itemLayout,
|
||||
pagination = {
|
||||
perPagePosts: 10
|
||||
}
|
||||
} = frontmatterPage
|
||||
|
||||
if (!listPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
extraPages.push({
|
||||
permalink: listPath,
|
||||
frontmatter
|
||||
})
|
||||
|
||||
frontmatterClassificationPages.push({
|
||||
id,
|
||||
pagination,
|
||||
keys
|
||||
})
|
||||
|
||||
pageEnhancers.push({
|
||||
when: ({ regularPath }) => regularPath === listPath,
|
||||
frontmatter: { layout: getLayout(listLayout) }
|
||||
})
|
||||
|
||||
pageEnhancers.push({
|
||||
when: ({ regularPath }) => regularPath && regularPath !== listPath && regularPath.startsWith(listPath),
|
||||
frontmatter: { layout: getLayout(itemLayout) }
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
pageEnhancers,
|
||||
frontmatterClassificationPages,
|
||||
extraPages,
|
||||
paginations
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleOptions
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
async function registerPagination (paginations, ctx) {
|
||||
ctx.paginations = []
|
||||
ctx.pageFilters = {}
|
||||
ctx.pageSorters = {}
|
||||
|
||||
function recordPageFilters (pid, filter) {
|
||||
if (ctx.pageFilters[pid]) {
|
||||
return
|
||||
}
|
||||
ctx.pageFilters[pid] = filter.toString()
|
||||
}
|
||||
|
||||
function recordPageSorters (pid, sorter) {
|
||||
if (ctx.pageSorters[pid]) {
|
||||
return
|
||||
}
|
||||
ctx.pageSorters[pid] = sorter.toString()
|
||||
}
|
||||
|
||||
for (const {
|
||||
pid,
|
||||
id,
|
||||
getUrl = (index => `/${id}/${index}/`),
|
||||
getTitle = (index => `Page ${index + 1} | ${id}`),
|
||||
options
|
||||
} of paginations) {
|
||||
const defaultPostsFilterMeta = {
|
||||
args: ['page'],
|
||||
body: `return page.pid === ${JSON.stringify(pid)} && page.id === ${JSON.stringify(id)}`
|
||||
}
|
||||
const defaultPostsFilter = new Function(defaultPostsFilterMeta.args, defaultPostsFilterMeta.body)
|
||||
const defaultPostsSorter = (prev, next) => {
|
||||
const prevTime = new Date(prev.frontmatter.date).getTime()
|
||||
const nextTime = new Date(next.frontmatter.date).getTime()
|
||||
return prevTime - nextTime > 0 ? -1 : 1
|
||||
}
|
||||
|
||||
const {
|
||||
perPagePosts = 10,
|
||||
layout = 'Layout',
|
||||
serverPageFilter = defaultPostsFilter,
|
||||
clientPageFilter = defaultPostsFilter,
|
||||
clientPageSorter = defaultPostsSorter
|
||||
} = options
|
||||
|
||||
const { pages: sourcePages } = ctx
|
||||
const pages = sourcePages.filter(serverPageFilter)
|
||||
|
||||
const intervallers = getIntervallers(pages.length, perPagePosts)
|
||||
const pagination = {
|
||||
pid,
|
||||
id,
|
||||
paginationPages: intervallers.map((interval, index) => {
|
||||
const path = getUrl(index)
|
||||
return { path, interval }
|
||||
})
|
||||
}
|
||||
|
||||
recordPageFilters(pid, clientPageFilter)
|
||||
recordPageSorters(pid, clientPageSorter)
|
||||
|
||||
await Promise.all(pagination.paginationPages.map(async ({ path }, index) => {
|
||||
if (index === 0) {
|
||||
return
|
||||
}
|
||||
return ctx.addPage({
|
||||
permalink: path,
|
||||
frontmatter: {
|
||||
layout,
|
||||
title: getTitle(index)
|
||||
}
|
||||
})
|
||||
}))
|
||||
ctx.paginations.push(pagination)
|
||||
}
|
||||
}
|
||||
|
||||
function getIntervallers (max, interval) {
|
||||
const count = max % interval === 0 ? Math.floor(max / interval) : Math.floor(max / interval) + 1
|
||||
const arr = [...Array(count)]
|
||||
return arr.map((v, index) => {
|
||||
const start = index * interval
|
||||
const end = (index + 1) * interval - 1
|
||||
return [start, end > max ? max : end]
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerPagination
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
const curryFrontmatterHandler = (scope, map) => (key, pageKey) => {
|
||||
if (key) {
|
||||
if (!map[key]) {
|
||||
map[key] = {}
|
||||
map[key].scope = scope
|
||||
map[key].path = `/${scope}/${key}/`
|
||||
map[key].pageKeys = []
|
||||
}
|
||||
map[key].pageKeys.push(pageKey)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
curryFrontmatterHandler
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
"vuepress",
|
||||
"generator"
|
||||
],
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1"
|
||||
},
|
||||
"author": "ULIVZ <chl814@foxmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import paginationMeta from '@dynamic/pagination'
|
||||
|
||||
class Pagination {
|
||||
constructor (pagination, { pages, route }) {
|
||||
let { postsFilter, postsSorter } = pagination
|
||||
|
||||
/* eslint-disable no-eval */
|
||||
postsFilter = eval(postsFilter)
|
||||
postsSorter = eval(postsSorter)
|
||||
|
||||
const { path } = route
|
||||
const { paginationPages } = pagination
|
||||
|
||||
paginationPages.forEach((page, index) => {
|
||||
if (page.path === path) {
|
||||
this.paginationIndex = index
|
||||
}
|
||||
})
|
||||
|
||||
if (!this.paginationIndex) {
|
||||
this.paginationIndex = 0
|
||||
}
|
||||
|
||||
this._paginationPages = paginationPages
|
||||
this._currentPage = paginationPages[this.paginationIndex]
|
||||
this._posts = pages.filter(postsFilter).sort(postsSorter)
|
||||
}
|
||||
|
||||
get length () {
|
||||
return this._paginationPages.length
|
||||
}
|
||||
|
||||
get posts () {
|
||||
const [start, end] = this._currentPage.interval
|
||||
return this._posts.slice(start, end + 1)
|
||||
}
|
||||
|
||||
get hasPrev () {
|
||||
return this.paginationIndex !== 0
|
||||
}
|
||||
|
||||
get prevLink () {
|
||||
if (this.hasPrev) {
|
||||
return this._paginationPages[this.paginationIndex - 1].path
|
||||
}
|
||||
}
|
||||
|
||||
get hasNext () {
|
||||
return this.paginationIndex !== this.length - 1
|
||||
}
|
||||
|
||||
get nextLink () {
|
||||
if (this.hasNext) {
|
||||
return this._paginationPages[this.paginationIndex + 1].path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ({ Vue }) => {
|
||||
Vue.mixin({
|
||||
computed: {
|
||||
$pagination () {
|
||||
const { pages } = this.$site
|
||||
const pagination = new Pagination(paginationMeta, {
|
||||
pages, route: this.$route
|
||||
})
|
||||
return pagination
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user