mirror of
https://github.com/wahyd4/vuepress.git
synced 2026-08-09 05:16:23 +10:00
- fix($core): markdown slot doesn‘t work. (regression of c85f62d)
- docs: fresh Node.JS API.
- test: official plugins.
258 lines
6.0 KiB
JavaScript
258 lines
6.0 KiB
JavaScript
'use strict'
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
const { inferDate, DATE_RE } = require('./util/index')
|
|
const {
|
|
fs,
|
|
path,
|
|
hash,
|
|
chalk,
|
|
logger,
|
|
slugify,
|
|
inferTitle,
|
|
fileToPath,
|
|
getPermalink,
|
|
extractHeaders,
|
|
parseFrontmatter,
|
|
parseVueFrontmatter: { parse: parseVueFrontmatter }
|
|
} = require('@vuepress/shared-utils')
|
|
|
|
/**
|
|
* Expose Page class.
|
|
*/
|
|
|
|
module.exports = class Page {
|
|
/**
|
|
* @param {string} path the URL (excluding the domain name) for your page/post.
|
|
* @param {string} title markdown title
|
|
* @param {string} content markdown file content
|
|
* @param {string} filePath absolute file path of source markdown file.
|
|
* @param {string} relative relative file path of source markdown file.
|
|
* @param {string} permalink same to path, the URL (excluding the domain name) for your page/post.
|
|
* @param {object} frontmatter
|
|
* @param {string} permalinkPattern
|
|
*/
|
|
|
|
constructor ({
|
|
path,
|
|
meta,
|
|
title,
|
|
content,
|
|
filePath,
|
|
relative,
|
|
permalink,
|
|
frontmatter = {},
|
|
permalinkPattern
|
|
}, context) {
|
|
this.title = title
|
|
this._meta = meta
|
|
this._filePath = filePath
|
|
this._content = content
|
|
this._permalink = permalink
|
|
this.frontmatter = frontmatter
|
|
this._permalinkPattern = permalinkPattern
|
|
this._context = context
|
|
|
|
if (relative) {
|
|
this.regularPath = encodeURI(fileToPath(relative))
|
|
} else if (path) {
|
|
this.regularPath = encodeURI(path)
|
|
} else if (permalink) {
|
|
this.regularPath = encodeURI(permalink)
|
|
}
|
|
|
|
this.key = 'v-' + hash(`${this._filePath}${this.regularPath}`)
|
|
// Using regularPath first, would be override by permalink later.
|
|
this.path = this.regularPath
|
|
}
|
|
|
|
/**
|
|
* Process a page.
|
|
*
|
|
* 1. If it's a page pointing to a md file, this method will try
|
|
* to resolve the page's title / headers from the content.
|
|
* 2. If it's a pure route. this method will only enhance it.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
async process ({
|
|
computed,
|
|
markdown,
|
|
enhancers = [],
|
|
preRender = {}
|
|
}) {
|
|
// relative path
|
|
let relPath
|
|
|
|
if (this._filePath) {
|
|
relPath = path.relative(this._context.sourceDir, this._filePath)
|
|
logger.developer(`static_route`, chalk.cyan(this.path))
|
|
this._content = await fs.readFile(this._filePath, 'utf-8')
|
|
} else if (this._content) {
|
|
logger.developer(`static_route`, chalk.cyan(this.path))
|
|
this._filePath = await this._context.writeTemp(`temp-pages/${this.key}.md`, this._content)
|
|
} else {
|
|
logger.developer(`dynamic_route`, chalk.cyan(this.path))
|
|
}
|
|
|
|
if (this._content) {
|
|
if (this._filePath.endsWith('.md')) {
|
|
const { excerpt, data, content } = parseFrontmatter(this._content)
|
|
this._strippedContent = content
|
|
Object.assign(this.frontmatter, data)
|
|
|
|
// infer title
|
|
const title = inferTitle(this.frontmatter, this._strippedContent)
|
|
if (title) {
|
|
this.title = title
|
|
}
|
|
|
|
// headers
|
|
const headers = extractHeaders(
|
|
this._strippedContent,
|
|
['h2', 'h3'],
|
|
markdown
|
|
)
|
|
if (headers.length) {
|
|
this.headers = headers
|
|
}
|
|
|
|
if (excerpt) {
|
|
const { html } = markdown.render(excerpt, {
|
|
frontmatter: this.frontmatter,
|
|
relPath
|
|
})
|
|
this.excerpt = html
|
|
}
|
|
} else if (this._filePath.endsWith('.vue')) {
|
|
const { data = {}} = parseVueFrontmatter(this._content)
|
|
// When Vue SFCs are source files, make them as layout components directly.
|
|
this.frontmatter = Object.assign({
|
|
layout: this.key
|
|
}, data)
|
|
}
|
|
}
|
|
|
|
// resolve i18n
|
|
computed.setPage(this)
|
|
this._computed = computed
|
|
this._localePath = computed.$localePath
|
|
|
|
this.enhance(enhancers)
|
|
this.buildPermalink()
|
|
}
|
|
|
|
/**
|
|
* file name of page's source markdown file, or the last cut of regularPath.
|
|
*
|
|
* @returns {string}
|
|
* @api public
|
|
*/
|
|
|
|
get filename () {
|
|
return path.parse(this._filePath || this.regularPath).name
|
|
}
|
|
|
|
/**
|
|
* slugified file name.
|
|
*
|
|
* @returns {string}
|
|
* @api public
|
|
*/
|
|
|
|
get slug () {
|
|
return slugify(this.strippedFilename)
|
|
}
|
|
|
|
/**
|
|
* stripped file name.
|
|
*
|
|
* If filename was yyyy-MM-dd-[title], the date prefix will be stripped.
|
|
* If filename was yyyy-MM-[title], the date prefix will be stripped.
|
|
*
|
|
* @returns {string}
|
|
* @api public
|
|
*/
|
|
|
|
get strippedFilename () {
|
|
const match = this.filename.match(DATE_RE)
|
|
return match ? match[3] : this.filename
|
|
}
|
|
|
|
/**
|
|
* get date of a page.
|
|
*
|
|
* @returns {null|string}
|
|
* @api public
|
|
*/
|
|
|
|
get date () {
|
|
return inferDate(this.frontmatter, this.filename)
|
|
}
|
|
|
|
/**
|
|
* Convert page's metadata to JSON, note that all fields beginning
|
|
* with an underscore will not be serialized.
|
|
*
|
|
* @returns {object}
|
|
* @api public
|
|
*/
|
|
|
|
toJson () {
|
|
const json = {}
|
|
Object.keys(this).reduce((json, key) => {
|
|
if (!key.startsWith('_')) {
|
|
json[key] = this[key]
|
|
}
|
|
return json
|
|
}, json)
|
|
return json
|
|
}
|
|
|
|
/**
|
|
* Build permalink via permalink pattern and page's metadata.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
buildPermalink () {
|
|
if (!this._permalink) {
|
|
this._permalink = getPermalink({
|
|
pattern: this.frontmatter.permalink || this._permalinkPattern,
|
|
slug: this.slug,
|
|
date: this.date,
|
|
localePath: this._localePath,
|
|
regularPath: this.regularPath
|
|
})
|
|
}
|
|
|
|
if (this._permalink) {
|
|
this.path = this._permalink
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute the page enhancers. A enhancer could do following things:
|
|
*
|
|
* 1. Modify page's frontmatter.
|
|
* 2. Add extra field to the page.
|
|
*
|
|
* @api private
|
|
*/
|
|
|
|
enhance (enhancers) {
|
|
for (const { name: pluginName, value: enhancer } of enhancers) {
|
|
try {
|
|
enhancer(this)
|
|
} catch (error) {
|
|
console.log(error)
|
|
throw new Error(`[${pluginName}] excuete extendPageData failed.`)
|
|
}
|
|
}
|
|
}
|
|
}
|