1
0
mirror of synced 2025-12-19 18:10:59 -05:00
Files
docs/lib/read-frontmatter.js

93 lines
2.6 KiB
JavaScript

import matter from 'gray-matter'
import Ajv from 'ajv'
import addErrors from 'ajv-errors'
import addFormats from 'ajv-formats'
import semver from 'semver'
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true })
ajv.addKeyword({
keyword: 'translatable',
})
ajv.addFormat('semver', {
validate: (x) => semver.validRange(x),
})
addErrors(ajv)
addFormats(ajv)
function readFrontmatter(markdown, opts = {}) {
const schema = opts.schema || { type: 'object', properties: {} }
const filepath = opts.filepath || null
let content, data
let errors = []
try {
;({ content, data } = matter(markdown))
} catch (e) {
const defaultReason = 'invalid frontmatter entry'
const reason = e.reason
? // make this common error message a little easier to understand
e.reason.startsWith('can not read a block mapping entry;')
? defaultReason
: e.reason
: defaultReason
const error = {
reason,
message: 'YML parsing error!',
}
if (filepath) error.filepath = filepath
errors.push(error)
console.warn(errors)
return { errors }
}
const ajvValidate = ajv.compile(schema)
const valid = ajvValidate(data)
if (!valid) {
errors = ajvValidate.errors
}
// Combine the AJV-supplied `instancePath` and `params` into a more user-friendly frontmatter path.
// For example, given:
// "instancePath": "/versions",
// "params": { "additionalProperty": "ftp" }
// return:
// property: 'versions.ftp'
//
// The purpose is to help users understand that the error is on the `fpt` key within the `versions` object.
// Note if the error is on a top-level FM property like `title`, the `instancePath` will be empty.
const cleanPropertyPath = (params, instancePath) => {
const mainProps = Object.values(params)[0]
if (!instancePath) return mainProps
const prefixProps = instancePath.replace('/', '').replace(/\//g, '.')
return typeof mainProps !== 'object' ? `${prefixProps}.${mainProps}` : prefixProps
}
if (!valid && filepath) {
errors = ajvValidate.errors.map((error) => {
const userFriendly = {}
userFriendly.property = cleanPropertyPath(error.params, error.instancePath)
userFriendly.message = error.message
userFriendly.reason = error.keyword
userFriendly.filepath = filepath
return userFriendly
})
}
return { content, data, errors }
}
// Expose gray-matter's underlying stringify method for joining a parsed
// frontmatter object and a markdown string back into a unified string
//
// stringify('some string', {some: 'frontmatter'})
readFrontmatter.stringify = matter.stringify
export default readFrontmatter