125 lines
3.5 KiB
JavaScript
125 lines
3.5 KiB
JavaScript
/**
|
||
* @typedef {'toml' | 'yaml'} Preset
|
||
* Known name of a frontmatter style.
|
||
*
|
||
* @typedef Info
|
||
* Sequence.
|
||
*
|
||
* Depending on how this structure is used, it reflects a marker or a fence.
|
||
* @property {string} close
|
||
* Closing.
|
||
* @property {string} open
|
||
* Opening.
|
||
*
|
||
* @typedef MatterProps
|
||
* Fields describing a kind of matter.
|
||
* @property {string} type
|
||
* Node type to tokenize as.
|
||
* @property {boolean | null | undefined} [anywhere=false]
|
||
* Whether matter can be found anywhere in the document, normally, only matter
|
||
* at the start of the document is recognized.
|
||
*
|
||
* > 👉 **Note**: using this is a terrible idea.
|
||
* > It’s called frontmatter, not matter-in-the-middle or so.
|
||
* > This makes your markdown less portable.
|
||
*
|
||
* @typedef MarkerProps
|
||
* Marker configuration.
|
||
* @property {Info | string} marker
|
||
* Character repeated 3 times, used as complete fences.
|
||
*
|
||
* For example the character `'-'` will result in `'---'` being used as the
|
||
* fence
|
||
* Pass `open` and `close` to specify different characters for opening and
|
||
* closing fences.
|
||
* @property {never} [fence]
|
||
* If `marker` is set, `fence` must not be set.
|
||
*
|
||
* @typedef FenceProps
|
||
* Fence configuration.
|
||
* @property {Info | string} fence
|
||
* Complete fences.
|
||
*
|
||
* This can be used when fences contain different characters or lengths
|
||
* other than 3.
|
||
* Pass `open` and `close` to interface to specify different characters for opening and
|
||
* closing fences.
|
||
* @property {never} [marker]
|
||
* If `fence` is set, `marker` must not be set.
|
||
*
|
||
* @typedef {(MatterProps & FenceProps) | (MatterProps & MarkerProps)} Matter
|
||
* Fields describing a kind of matter.
|
||
*
|
||
* > 👉 **Note**: using `anywhere` is a terrible idea.
|
||
* > It’s called frontmatter, not matter-in-the-middle or so.
|
||
* > This makes your markdown less portable.
|
||
*
|
||
* > 👉 **Note**: `marker` and `fence` are mutually exclusive.
|
||
* > If `marker` is set, `fence` must not be set, and vice versa.
|
||
*
|
||
* @typedef {Matter | Preset | Array<Matter | Preset>} Options
|
||
* Configuration.
|
||
*/
|
||
|
||
import {fault} from 'fault'
|
||
const own = {}.hasOwnProperty
|
||
const markers = {
|
||
yaml: '-',
|
||
toml: '+'
|
||
}
|
||
|
||
/**
|
||
* Simplify options by normalizing them to an array of matters.
|
||
*
|
||
* @param {Options | null | undefined} [options='yaml']
|
||
* Configuration (default: `'yaml'`).
|
||
* @returns {Array<Matter>}
|
||
* List of matters.
|
||
*/
|
||
export function toMatters(options) {
|
||
/** @type {Array<Matter>} */
|
||
const result = []
|
||
let index = -1
|
||
|
||
/** @type {Array<Matter | Preset>} */
|
||
const presetsOrMatters = Array.isArray(options)
|
||
? options
|
||
: options
|
||
? [options]
|
||
: ['yaml']
|
||
while (++index < presetsOrMatters.length) {
|
||
result[index] = matter(presetsOrMatters[index])
|
||
}
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* Simplify an option.
|
||
*
|
||
* @param {Matter | Preset} option
|
||
* Configuration.
|
||
* @returns {Matter}
|
||
* Matter.
|
||
*/
|
||
function matter(option) {
|
||
let result = option
|
||
if (typeof result === 'string') {
|
||
if (!own.call(markers, result)) {
|
||
throw fault('Missing matter definition for `%s`', result)
|
||
}
|
||
result = {
|
||
type: result,
|
||
marker: markers[result]
|
||
}
|
||
} else if (typeof result !== 'object') {
|
||
throw fault('Expected matter to be an object, not `%j`', result)
|
||
}
|
||
if (!own.call(result, 'type')) {
|
||
throw fault('Missing `type` in matter `%j`', result)
|
||
}
|
||
if (!own.call(result, 'fence') && !own.call(result, 'marker')) {
|
||
throw fault('Missing `marker` or `fence` in matter `%j`', result)
|
||
}
|
||
return result
|
||
}
|