123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- /**
- * @typedef {import('mdast').Nodes} Nodes
- * @typedef {import('./types.js').Enter} Enter
- * @typedef {import('./types.js').Info} Info
- * @typedef {import('./types.js').Join} Join
- * @typedef {import('./types.js').FlowParents} FlowParents
- * @typedef {import('./types.js').Options} Options
- * @typedef {import('./types.js').PhrasingParents} PhrasingParents
- * @typedef {import('./types.js').SafeConfig} SafeConfig
- * @typedef {import('./types.js').State} State
- * @typedef {import('./types.js').TrackFields} TrackFields
- */
- import {zwitch} from 'zwitch'
- import {configure} from './configure.js'
- import {handle as handlers} from './handle/index.js'
- import {join} from './join.js'
- import {unsafe} from './unsafe.js'
- import {association} from './util/association.js'
- import {compilePattern} from './util/compile-pattern.js'
- import {containerPhrasing} from './util/container-phrasing.js'
- import {containerFlow} from './util/container-flow.js'
- import {indentLines} from './util/indent-lines.js'
- import {safe} from './util/safe.js'
- import {track} from './util/track.js'
- /**
- * Turn an mdast syntax tree into markdown.
- *
- * @param {Nodes} tree
- * Tree to serialize.
- * @param {Options} [options]
- * Configuration (optional).
- * @returns {string}
- * Serialized markdown representing `tree`.
- */
- export function toMarkdown(tree, options = {}) {
- /** @type {State} */
- const state = {
- enter,
- indentLines,
- associationId: association,
- containerPhrasing: containerPhrasingBound,
- containerFlow: containerFlowBound,
- createTracker: track,
- compilePattern,
- safe: safeBound,
- stack: [],
- unsafe: [...unsafe],
- join: [...join],
- // @ts-expect-error: GFM / frontmatter are typed in `mdast` but not defined
- // here.
- handlers: {...handlers},
- options: {},
- indexStack: [],
- // @ts-expect-error: add `handle` in a second.
- handle: undefined
- }
- configure(state, options)
- if (state.options.tightDefinitions) {
- state.join.push(joinDefinition)
- }
- state.handle = zwitch('type', {
- invalid,
- unknown,
- handlers: state.handlers
- })
- let result = state.handle(tree, undefined, state, {
- before: '\n',
- after: '\n',
- now: {line: 1, column: 1},
- lineShift: 0
- })
- if (
- result &&
- result.charCodeAt(result.length - 1) !== 10 &&
- result.charCodeAt(result.length - 1) !== 13
- ) {
- result += '\n'
- }
- return result
- /** @type {Enter} */
- function enter(name) {
- state.stack.push(name)
- return exit
- /**
- * @returns {undefined}
- */
- function exit() {
- state.stack.pop()
- }
- }
- }
- /**
- * @param {unknown} value
- * @returns {never}
- */
- function invalid(value) {
- throw new Error('Cannot handle value `' + value + '`, expected node')
- }
- /**
- * @param {unknown} value
- * @returns {never}
- */
- function unknown(value) {
- // Always a node.
- const node = /** @type {Nodes} */ (value)
- throw new Error('Cannot handle unknown node `' + node.type + '`')
- }
- /** @type {Join} */
- function joinDefinition(left, right) {
- // No blank line between adjacent definitions.
- if (left.type === 'definition' && left.type === right.type) {
- return 0
- }
- }
- /**
- * Serialize the children of a parent that contains phrasing children.
- *
- * These children will be joined flush together.
- *
- * @this {State}
- * Info passed around about the current state.
- * @param {PhrasingParents} parent
- * Parent of flow nodes.
- * @param {Info} info
- * Info on where we are in the document we are generating.
- * @returns {string}
- * Serialized children, joined together.
- */
- function containerPhrasingBound(parent, info) {
- return containerPhrasing(parent, this, info)
- }
- /**
- * Serialize the children of a parent that contains flow children.
- *
- * These children will typically be joined by blank lines.
- * What they are joined by exactly is defined by `Join` functions.
- *
- * @this {State}
- * Info passed around about the current state.
- * @param {FlowParents} parent
- * Parent of flow nodes.
- * @param {TrackFields} info
- * Info on where we are in the document we are generating.
- * @returns {string}
- * Serialized children, joined by (blank) lines.
- */
- function containerFlowBound(parent, info) {
- return containerFlow(parent, this, info)
- }
- /**
- * Make a string safe for embedding in markdown constructs.
- *
- * In markdown, almost all punctuation characters can, in certain cases,
- * result in something.
- * Whether they do is highly subjective to where they happen and in what
- * they happen.
- *
- * To solve this, `mdast-util-to-markdown` tracks:
- *
- * * Characters before and after something;
- * * What “constructs” we are in.
- *
- * This information is then used by this function to escape or encode
- * special characters.
- *
- * @this {State}
- * Info passed around about the current state.
- * @param {string | null | undefined} value
- * Raw value to make safe.
- * @param {SafeConfig} config
- * Configuration.
- * @returns {string}
- * Serialized markdown safe for embedding.
- */
- function safeBound(value, config) {
- return safe(this, value, config)
- }
|