/** * @typedef {import('./types.js').Node} Node * @typedef {import('./types.js').Options} Options * @typedef {import('./types.js').Context} Context * @typedef {import('./types.js').Handle} Handle * @typedef {import('./types.js').Join} Join * @typedef {import('./types.js').Unsafe} Unsafe */ import {zwitch} from 'zwitch' import {configure} from './configure.js' import {handle} from './handle/index.js' import {join} from './join.js' import {unsafe} from './unsafe.js' /** * @param {Node} tree * @param {Options} [options] * @returns {string} */ export function toMarkdown(tree, options = {}) { /** @type {Context} */ // @ts-expect-error: we’ll add `handle` later. const context = { enter, stack: [], unsafe: [], join: [], handlers: {}, options: {}, indexStack: [] } configure(context, {unsafe, join, handlers: handle}) configure(context, options) if (context.options.tightDefinitions) { configure(context, {join: [joinDefinition]}) } /** @type {Handle} */ context.handle = zwitch('type', { invalid, // @ts-expect-error: hush. unknown, // @ts-expect-error: hush. handlers: context.handlers }) let result = context.handle(tree, null, context, { 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 {Context['enter']} */ function enter(name) { context.stack.push(name) return exit function exit() { context.stack.pop() } } } /** * @type {Handle} * @param {unknown} value */ function invalid(value) { throw new Error('Cannot handle value `' + value + '`, expected node') } /** * @type {Handle} * @param {Node} node */ function unknown(node) { 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 } }