/** * @typedef {import('hast').Element} Element * @typedef {import('hast').Parents} Parents */ import {whitespace} from 'hast-util-whitespace' import {siblingAfter} from './util/siblings.js' import {omission} from './omission.js' export const closing = omission({ body, caption: headOrColgroupOrCaption, colgroup: headOrColgroupOrCaption, dd, dt, head: headOrColgroupOrCaption, html, li, optgroup, option, p, rp: rubyElement, rt: rubyElement, tbody, td: cells, tfoot, th: cells, thead, tr }) /** * Macro for ``, ``, and ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function headOrColgroupOrCaption(_, index, parent) { const next = siblingAfter(parent, index, true) return ( !next || (next.type !== 'comment' && !(next.type === 'text' && whitespace(next.value.charAt(0)))) ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function html(_, index, parent) { const next = siblingAfter(parent, index) return !next || next.type !== 'comment' } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function body(_, index, parent) { const next = siblingAfter(parent, index) return !next || next.type !== 'comment' } /** * Whether to omit `

`. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function p(_, index, parent) { const next = siblingAfter(parent, index) return next ? next.type === 'element' && (next.tagName === 'address' || next.tagName === 'article' || next.tagName === 'aside' || next.tagName === 'blockquote' || next.tagName === 'details' || next.tagName === 'div' || next.tagName === 'dl' || next.tagName === 'fieldset' || next.tagName === 'figcaption' || next.tagName === 'figure' || next.tagName === 'footer' || next.tagName === 'form' || next.tagName === 'h1' || next.tagName === 'h2' || next.tagName === 'h3' || next.tagName === 'h4' || next.tagName === 'h5' || next.tagName === 'h6' || next.tagName === 'header' || next.tagName === 'hgroup' || next.tagName === 'hr' || next.tagName === 'main' || next.tagName === 'menu' || next.tagName === 'nav' || next.tagName === 'ol' || next.tagName === 'p' || next.tagName === 'pre' || next.tagName === 'section' || next.tagName === 'table' || next.tagName === 'ul') : !parent || // Confusing parent. !( parent.type === 'element' && (parent.tagName === 'a' || parent.tagName === 'audio' || parent.tagName === 'del' || parent.tagName === 'ins' || parent.tagName === 'map' || parent.tagName === 'noscript' || parent.tagName === 'video') ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function li(_, index, parent) { const next = siblingAfter(parent, index) return !next || (next.type === 'element' && next.tagName === 'li') } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function dt(_, index, parent) { const next = siblingAfter(parent, index) return Boolean( next && next.type === 'element' && (next.tagName === 'dt' || next.tagName === 'dd') ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function dd(_, index, parent) { const next = siblingAfter(parent, index) return ( !next || (next.type === 'element' && (next.tagName === 'dt' || next.tagName === 'dd')) ) } /** * Whether to omit `` or ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function rubyElement(_, index, parent) { const next = siblingAfter(parent, index) return ( !next || (next.type === 'element' && (next.tagName === 'rp' || next.tagName === 'rt')) ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function optgroup(_, index, parent) { const next = siblingAfter(parent, index) return !next || (next.type === 'element' && next.tagName === 'optgroup') } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function option(_, index, parent) { const next = siblingAfter(parent, index) return ( !next || (next.type === 'element' && (next.tagName === 'option' || next.tagName === 'optgroup')) ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function thead(_, index, parent) { const next = siblingAfter(parent, index) return Boolean( next && next.type === 'element' && (next.tagName === 'tbody' || next.tagName === 'tfoot') ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function tbody(_, index, parent) { const next = siblingAfter(parent, index) return ( !next || (next.type === 'element' && (next.tagName === 'tbody' || next.tagName === 'tfoot')) ) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function tfoot(_, index, parent) { return !siblingAfter(parent, index) } /** * Whether to omit ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function tr(_, index, parent) { const next = siblingAfter(parent, index) return !next || (next.type === 'element' && next.tagName === 'tr') } /** * Whether to omit `` or ``. * * @param {Element} _ * Element. * @param {number | undefined} index * Index of element in parent. * @param {Parents | undefined} parent * Parent of element. * @returns {boolean} * Whether the closing tag can be omitted. */ function cells(_, index, parent) { const next = siblingAfter(parent, index) return ( !next || (next.type === 'element' && (next.tagName === 'td' || next.tagName === 'th')) ) }