/** * @typedef {import('micromark-util-types').Extension} Extension * @typedef {import('micromark-util-types').Handles} Handles * @typedef {import('micromark-util-types').HtmlExtension} HtmlExtension * @typedef {import('micromark-util-types').NormalizedExtension} NormalizedExtension */ import {splice} from 'micromark-util-chunked' const hasOwnProperty = {}.hasOwnProperty /** * Combine multiple syntax extensions into one. * * @param {Array} extensions * List of syntax extensions. * @returns {NormalizedExtension} * A single combined extension. */ export function combineExtensions(extensions) { /** @type {NormalizedExtension} */ const all = {} let index = -1 while (++index < extensions.length) { syntaxExtension(all, extensions[index]) } return all } /** * Merge `extension` into `all`. * * @param {NormalizedExtension} all * Extension to merge into. * @param {Extension} extension * Extension to merge. * @returns {undefined} */ function syntaxExtension(all, extension) { /** @type {keyof Extension} */ let hook for (hook in extension) { const maybe = hasOwnProperty.call(all, hook) ? all[hook] : undefined /** @type {Record} */ const left = maybe || (all[hook] = {}) /** @type {Record | undefined} */ const right = extension[hook] /** @type {string} */ let code if (right) { for (code in right) { if (!hasOwnProperty.call(left, code)) left[code] = [] const value = right[code] constructs( // @ts-expect-error Looks like a list. left[code], Array.isArray(value) ? value : value ? [value] : [] ) } } } } /** * Merge `list` into `existing` (both lists of constructs). * Mutates `existing`. * * @param {Array} existing * @param {Array} list * @returns {undefined} */ function constructs(existing, list) { let index = -1 /** @type {Array} */ const before = [] while (++index < list.length) { // @ts-expect-error Looks like an object. ;(list[index].add === 'after' ? existing : before).push(list[index]) } splice(existing, 0, 0, before) } /** * Combine multiple HTML extensions into one. * * @param {Array} htmlExtensions * List of HTML extensions. * @returns {HtmlExtension} * A single combined HTML extension. */ export function combineHtmlExtensions(htmlExtensions) { /** @type {HtmlExtension} */ const handlers = {} let index = -1 while (++index < htmlExtensions.length) { htmlExtension(handlers, htmlExtensions[index]) } return handlers } /** * Merge `extension` into `all`. * * @param {HtmlExtension} all * Extension to merge into. * @param {HtmlExtension} extension * Extension to merge. * @returns {undefined} */ function htmlExtension(all, extension) { /** @type {keyof HtmlExtension} */ let hook for (hook in extension) { const maybe = hasOwnProperty.call(all, hook) ? all[hook] : undefined const left = maybe || (all[hook] = {}) const right = extension[hook] /** @type {keyof Handles} */ let type if (right) { for (type in right) { // @ts-expect-error assume document vs regular handler are managed correctly. left[type] = right[type] } } } }