site/node_modules/hast-util-to-html/lib/omission/opening.js
2024-10-14 08:09:33 +02:00

148 lines
3.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Parents} Parents
*/
import {whitespace} from 'hast-util-whitespace'
import {siblingAfter, siblingBefore} from './util/siblings.js'
import {closing} from './closing.js'
import {omission} from './omission.js'
export const opening = omission({
body,
colgroup,
head,
html,
tbody
})
/**
* Whether to omit `<html>`.
*
* @param {Element} node
* Element.
* @returns {boolean}
* Whether the opening tag can be omitted.
*/
function html(node) {
const head = siblingAfter(node, -1)
return !head || head.type !== 'comment'
}
/**
* Whether to omit `<head>`.
*
* @param {Element} node
* Element.
* @returns {boolean}
* Whether the opening tag can be omitted.
*/
function head(node) {
const children = node.children
/** @type {Array<string>} */
const seen = []
let index = -1
while (++index < children.length) {
const child = children[index]
if (
child.type === 'element' &&
(child.tagName === 'title' || child.tagName === 'base')
) {
if (seen.includes(child.tagName)) return false
seen.push(child.tagName)
}
}
return children.length > 0
}
/**
* Whether to omit `<body>`.
*
* @param {Element} node
* Element.
* @returns {boolean}
* Whether the opening tag can be omitted.
*/
function body(node) {
const head = siblingAfter(node, -1, true)
return (
!head ||
(head.type !== 'comment' &&
!(head.type === 'text' && whitespace(head.value.charAt(0))) &&
!(
head.type === 'element' &&
(head.tagName === 'meta' ||
head.tagName === 'link' ||
head.tagName === 'script' ||
head.tagName === 'style' ||
head.tagName === 'template')
))
)
}
/**
* Whether to omit `<colgroup>`.
* The spec describes some logic for the opening tag, but its easier to
* implement in the closing tag, to the same effect, so we handle it there
* instead.
*
* @param {Element} node
* Element.
* @param {number | undefined} index
* Index of element in parent.
* @param {Parents | undefined} parent
* Parent of element.
* @returns {boolean}
* Whether the opening tag can be omitted.
*/
function colgroup(node, index, parent) {
const previous = siblingBefore(parent, index)
const head = siblingAfter(node, -1, true)
// Previous colgroup was already omitted.
if (
parent &&
previous &&
previous.type === 'element' &&
previous.tagName === 'colgroup' &&
closing(previous, parent.children.indexOf(previous), parent)
) {
return false
}
return Boolean(head && head.type === 'element' && head.tagName === 'col')
}
/**
* Whether to omit `<tbody>`.
*
* @param {Element} node
* Element.
* @param {number | undefined} index
* Index of element in parent.
* @param {Parents | undefined} parent
* Parent of element.
* @returns {boolean}
* Whether the opening tag can be omitted.
*/
function tbody(node, index, parent) {
const previous = siblingBefore(parent, index)
const head = siblingAfter(node, -1)
// Previous table section was already omitted.
if (
parent &&
previous &&
previous.type === 'element' &&
(previous.tagName === 'thead' || previous.tagName === 'tbody') &&
closing(previous, parent.children.indexOf(previous), parent)
) {
return false
}
return Boolean(head && head.type === 'element' && head.tagName === 'tr')
}