rehype-table-merge
Version:
A rehype plugin for merging cells of table elements.
1,985 lines (1,841 loc) • 126 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**
* @typedef {import('./info.js').Info} Info
* @typedef {Record<string, Info>} Properties
* @typedef {Record<string, string>} Normal
*/
class Schema {
/**
* @constructor
* @param {Properties} property
* @param {Normal} normal
* @param {string} [space]
*/
constructor(property, normal, space) {
this.property = property;
this.normal = normal;
if (space) {
this.space = space;
}
}
}
/** @type {Properties} */
Schema.prototype.property = {};
/** @type {Normal} */
Schema.prototype.normal = {};
/** @type {string|null} */
Schema.prototype.space = null;
/**
* @typedef {import('./schema.js').Properties} Properties
* @typedef {import('./schema.js').Normal} Normal
*/
/**
* @param {Schema[]} definitions
* @param {string} [space]
* @returns {Schema}
*/
function merge(definitions, space) {
/** @type {Properties} */
const property = {};
/** @type {Normal} */
const normal = {};
let index = -1;
while (++index < definitions.length) {
Object.assign(property, definitions[index].property);
Object.assign(normal, definitions[index].normal);
}
return new Schema(property, normal, space)
}
/**
* @param {string} value
* @returns {string}
*/
function normalize(value) {
return value.toLowerCase()
}
class Info {
/**
* @constructor
* @param {string} property
* @param {string} attribute
*/
constructor(property, attribute) {
/** @type {string} */
this.property = property;
/** @type {string} */
this.attribute = attribute;
}
}
/** @type {string|null} */
Info.prototype.space = null;
Info.prototype.boolean = false;
Info.prototype.booleanish = false;
Info.prototype.overloadedBoolean = false;
Info.prototype.number = false;
Info.prototype.commaSeparated = false;
Info.prototype.spaceSeparated = false;
Info.prototype.commaOrSpaceSeparated = false;
Info.prototype.mustUseProperty = false;
Info.prototype.defined = false;
let powers = 0;
const boolean = increment();
const booleanish = increment();
const overloadedBoolean = increment();
const number = increment();
const spaceSeparated = increment();
const commaSeparated = increment();
const commaOrSpaceSeparated = increment();
function increment() {
return 2 ** ++powers
}
var types = /*#__PURE__*/Object.freeze({
__proto__: null,
boolean: boolean,
booleanish: booleanish,
overloadedBoolean: overloadedBoolean,
number: number,
spaceSeparated: spaceSeparated,
commaSeparated: commaSeparated,
commaOrSpaceSeparated: commaOrSpaceSeparated
});
/** @type {Array<keyof types>} */
// @ts-expect-error: hush.
const checks = Object.keys(types);
class DefinedInfo extends Info {
/**
* @constructor
* @param {string} property
* @param {string} attribute
* @param {number|null} [mask]
* @param {string} [space]
*/
constructor(property, attribute, mask, space) {
let index = -1;
super(property, attribute);
mark(this, 'space', space);
if (typeof mask === 'number') {
while (++index < checks.length) {
const check = checks[index];
mark(this, checks[index], (mask & types[check]) === types[check]);
}
}
}
}
DefinedInfo.prototype.defined = true;
/**
* @param {DefinedInfo} values
* @param {string} key
* @param {unknown} value
*/
function mark(values, key, value) {
if (value) {
// @ts-expect-error: assume `value` matches the expected value of `key`.
values[key] = value;
}
}
/**
* @typedef {import('./schema.js').Properties} Properties
* @typedef {import('./schema.js').Normal} Normal
*
* @typedef {Record<string, string>} Attributes
*
* @typedef {Object} Definition
* @property {Record<string, number|null>} properties
* @property {(attributes: Attributes, property: string) => string} transform
* @property {string} [space]
* @property {Attributes} [attributes]
* @property {Array<string>} [mustUseProperty]
*/
const own$3 = {}.hasOwnProperty;
/**
* @param {Definition} definition
* @returns {Schema}
*/
function create(definition) {
/** @type {Properties} */
const property = {};
/** @type {Normal} */
const normal = {};
/** @type {string} */
let prop;
for (prop in definition.properties) {
if (own$3.call(definition.properties, prop)) {
const value = definition.properties[prop];
const info = new DefinedInfo(
prop,
definition.transform(definition.attributes || {}, prop),
value,
definition.space
);
if (
definition.mustUseProperty &&
definition.mustUseProperty.includes(prop)
) {
info.mustUseProperty = true;
}
property[prop] = info;
normal[normalize(prop)] = prop;
normal[normalize(info.attribute)] = prop;
}
}
return new Schema(property, normal, definition.space)
}
const xlink = create({
space: 'xlink',
transform(_, prop) {
return 'xlink:' + prop.slice(5).toLowerCase()
},
properties: {
xLinkActuate: null,
xLinkArcRole: null,
xLinkHref: null,
xLinkRole: null,
xLinkShow: null,
xLinkTitle: null,
xLinkType: null
}
});
const xml = create({
space: 'xml',
transform(_, prop) {
return 'xml:' + prop.slice(3).toLowerCase()
},
properties: {xmlLang: null, xmlBase: null, xmlSpace: null}
});
/**
* @param {Record<string, string>} attributes
* @param {string} attribute
* @returns {string}
*/
function caseSensitiveTransform(attributes, attribute) {
return attribute in attributes ? attributes[attribute] : attribute
}
/**
* @param {Record<string, string>} attributes
* @param {string} property
* @returns {string}
*/
function caseInsensitiveTransform(attributes, property) {
return caseSensitiveTransform(attributes, property.toLowerCase())
}
const xmlns = create({
space: 'xmlns',
attributes: {xmlnsxlink: 'xmlns:xlink'},
transform: caseInsensitiveTransform,
properties: {xmlns: null, xmlnsXLink: null}
});
const aria = create({
transform(_, prop) {
return prop === 'role' ? prop : 'aria-' + prop.slice(4).toLowerCase()
},
properties: {
ariaActiveDescendant: null,
ariaAtomic: booleanish,
ariaAutoComplete: null,
ariaBusy: booleanish,
ariaChecked: booleanish,
ariaColCount: number,
ariaColIndex: number,
ariaColSpan: number,
ariaControls: spaceSeparated,
ariaCurrent: null,
ariaDescribedBy: spaceSeparated,
ariaDetails: null,
ariaDisabled: booleanish,
ariaDropEffect: spaceSeparated,
ariaErrorMessage: null,
ariaExpanded: booleanish,
ariaFlowTo: spaceSeparated,
ariaGrabbed: booleanish,
ariaHasPopup: null,
ariaHidden: booleanish,
ariaInvalid: null,
ariaKeyShortcuts: null,
ariaLabel: null,
ariaLabelledBy: spaceSeparated,
ariaLevel: number,
ariaLive: null,
ariaModal: booleanish,
ariaMultiLine: booleanish,
ariaMultiSelectable: booleanish,
ariaOrientation: null,
ariaOwns: spaceSeparated,
ariaPlaceholder: null,
ariaPosInSet: number,
ariaPressed: booleanish,
ariaReadOnly: booleanish,
ariaRelevant: null,
ariaRequired: booleanish,
ariaRoleDescription: spaceSeparated,
ariaRowCount: number,
ariaRowIndex: number,
ariaRowSpan: number,
ariaSelected: booleanish,
ariaSetSize: number,
ariaSort: null,
ariaValueMax: number,
ariaValueMin: number,
ariaValueNow: number,
ariaValueText: null,
role: null
}
});
const html$1 = create({
space: 'html',
attributes: {
acceptcharset: 'accept-charset',
classname: 'class',
htmlfor: 'for',
httpequiv: 'http-equiv'
},
transform: caseInsensitiveTransform,
mustUseProperty: ['checked', 'multiple', 'muted', 'selected'],
properties: {
// Standard Properties.
abbr: null,
accept: commaSeparated,
acceptCharset: spaceSeparated,
accessKey: spaceSeparated,
action: null,
allow: null,
allowFullScreen: boolean,
allowPaymentRequest: boolean,
allowUserMedia: boolean,
alt: null,
as: null,
async: boolean,
autoCapitalize: null,
autoComplete: spaceSeparated,
autoFocus: boolean,
autoPlay: boolean,
capture: boolean,
charSet: null,
checked: boolean,
cite: null,
className: spaceSeparated,
cols: number,
colSpan: null,
content: null,
contentEditable: booleanish,
controls: boolean,
controlsList: spaceSeparated,
coords: number | commaSeparated,
crossOrigin: null,
data: null,
dateTime: null,
decoding: null,
default: boolean,
defer: boolean,
dir: null,
dirName: null,
disabled: boolean,
download: overloadedBoolean,
draggable: booleanish,
encType: null,
enterKeyHint: null,
form: null,
formAction: null,
formEncType: null,
formMethod: null,
formNoValidate: boolean,
formTarget: null,
headers: spaceSeparated,
height: number,
hidden: boolean,
high: number,
href: null,
hrefLang: null,
htmlFor: spaceSeparated,
httpEquiv: spaceSeparated,
id: null,
imageSizes: null,
imageSrcSet: null,
inputMode: null,
integrity: null,
is: null,
isMap: boolean,
itemId: null,
itemProp: spaceSeparated,
itemRef: spaceSeparated,
itemScope: boolean,
itemType: spaceSeparated,
kind: null,
label: null,
lang: null,
language: null,
list: null,
loading: null,
loop: boolean,
low: number,
manifest: null,
max: null,
maxLength: number,
media: null,
method: null,
min: null,
minLength: number,
multiple: boolean,
muted: boolean,
name: null,
nonce: null,
noModule: boolean,
noValidate: boolean,
onAbort: null,
onAfterPrint: null,
onAuxClick: null,
onBeforePrint: null,
onBeforeUnload: null,
onBlur: null,
onCancel: null,
onCanPlay: null,
onCanPlayThrough: null,
onChange: null,
onClick: null,
onClose: null,
onContextLost: null,
onContextMenu: null,
onContextRestored: null,
onCopy: null,
onCueChange: null,
onCut: null,
onDblClick: null,
onDrag: null,
onDragEnd: null,
onDragEnter: null,
onDragExit: null,
onDragLeave: null,
onDragOver: null,
onDragStart: null,
onDrop: null,
onDurationChange: null,
onEmptied: null,
onEnded: null,
onError: null,
onFocus: null,
onFormData: null,
onHashChange: null,
onInput: null,
onInvalid: null,
onKeyDown: null,
onKeyPress: null,
onKeyUp: null,
onLanguageChange: null,
onLoad: null,
onLoadedData: null,
onLoadedMetadata: null,
onLoadEnd: null,
onLoadStart: null,
onMessage: null,
onMessageError: null,
onMouseDown: null,
onMouseEnter: null,
onMouseLeave: null,
onMouseMove: null,
onMouseOut: null,
onMouseOver: null,
onMouseUp: null,
onOffline: null,
onOnline: null,
onPageHide: null,
onPageShow: null,
onPaste: null,
onPause: null,
onPlay: null,
onPlaying: null,
onPopState: null,
onProgress: null,
onRateChange: null,
onRejectionHandled: null,
onReset: null,
onResize: null,
onScroll: null,
onSecurityPolicyViolation: null,
onSeeked: null,
onSeeking: null,
onSelect: null,
onSlotChange: null,
onStalled: null,
onStorage: null,
onSubmit: null,
onSuspend: null,
onTimeUpdate: null,
onToggle: null,
onUnhandledRejection: null,
onUnload: null,
onVolumeChange: null,
onWaiting: null,
onWheel: null,
open: boolean,
optimum: number,
pattern: null,
ping: spaceSeparated,
placeholder: null,
playsInline: boolean,
poster: null,
preload: null,
readOnly: boolean,
referrerPolicy: null,
rel: spaceSeparated,
required: boolean,
reversed: boolean,
rows: number,
rowSpan: number,
sandbox: spaceSeparated,
scope: null,
scoped: boolean,
seamless: boolean,
selected: boolean,
shape: null,
size: number,
sizes: null,
slot: null,
span: number,
spellCheck: booleanish,
src: null,
srcDoc: null,
srcLang: null,
srcSet: null,
start: number,
step: null,
style: null,
tabIndex: number,
target: null,
title: null,
translate: null,
type: null,
typeMustMatch: boolean,
useMap: null,
value: booleanish,
width: number,
wrap: null,
// Legacy.
// See: https://html.spec.whatwg.org/#other-elements,-attributes-and-apis
align: null, // Several. Use CSS `text-align` instead,
aLink: null, // `<body>`. Use CSS `a:active {color}` instead
archive: spaceSeparated, // `<object>`. List of URIs to archives
axis: null, // `<td>` and `<th>`. Use `scope` on `<th>`
background: null, // `<body>`. Use CSS `background-image` instead
bgColor: null, // `<body>` and table elements. Use CSS `background-color` instead
border: number, // `<table>`. Use CSS `border-width` instead,
borderColor: null, // `<table>`. Use CSS `border-color` instead,
bottomMargin: number, // `<body>`
cellPadding: null, // `<table>`
cellSpacing: null, // `<table>`
char: null, // Several table elements. When `align=char`, sets the character to align on
charOff: null, // Several table elements. When `char`, offsets the alignment
classId: null, // `<object>`
clear: null, // `<br>`. Use CSS `clear` instead
code: null, // `<object>`
codeBase: null, // `<object>`
codeType: null, // `<object>`
color: null, // `<font>` and `<hr>`. Use CSS instead
compact: boolean, // Lists. Use CSS to reduce space between items instead
declare: boolean, // `<object>`
event: null, // `<script>`
face: null, // `<font>`. Use CSS instead
frame: null, // `<table>`
frameBorder: null, // `<iframe>`. Use CSS `border` instead
hSpace: number, // `<img>` and `<object>`
leftMargin: number, // `<body>`
link: null, // `<body>`. Use CSS `a:link {color: *}` instead
longDesc: null, // `<frame>`, `<iframe>`, and `<img>`. Use an `<a>`
lowSrc: null, // `<img>`. Use a `<picture>`
marginHeight: number, // `<body>`
marginWidth: number, // `<body>`
noResize: boolean, // `<frame>`
noHref: boolean, // `<area>`. Use no href instead of an explicit `nohref`
noShade: boolean, // `<hr>`. Use background-color and height instead of borders
noWrap: boolean, // `<td>` and `<th>`
object: null, // `<applet>`
profile: null, // `<head>`
prompt: null, // `<isindex>`
rev: null, // `<link>`
rightMargin: number, // `<body>`
rules: null, // `<table>`
scheme: null, // `<meta>`
scrolling: booleanish, // `<frame>`. Use overflow in the child context
standby: null, // `<object>`
summary: null, // `<table>`
text: null, // `<body>`. Use CSS `color` instead
topMargin: number, // `<body>`
valueType: null, // `<param>`
version: null, // `<html>`. Use a doctype.
vAlign: null, // Several. Use CSS `vertical-align` instead
vLink: null, // `<body>`. Use CSS `a:visited {color}` instead
vSpace: number, // `<img>` and `<object>`
// Non-standard Properties.
allowTransparency: null,
autoCorrect: null,
autoSave: null,
disablePictureInPicture: boolean,
disableRemotePlayback: boolean,
prefix: null,
property: null,
results: number,
security: null,
unselectable: null
}
});
const svg$1 = create({
space: 'svg',
attributes: {
accentHeight: 'accent-height',
alignmentBaseline: 'alignment-baseline',
arabicForm: 'arabic-form',
baselineShift: 'baseline-shift',
capHeight: 'cap-height',
className: 'class',
clipPath: 'clip-path',
clipRule: 'clip-rule',
colorInterpolation: 'color-interpolation',
colorInterpolationFilters: 'color-interpolation-filters',
colorProfile: 'color-profile',
colorRendering: 'color-rendering',
crossOrigin: 'crossorigin',
dataType: 'datatype',
dominantBaseline: 'dominant-baseline',
enableBackground: 'enable-background',
fillOpacity: 'fill-opacity',
fillRule: 'fill-rule',
floodColor: 'flood-color',
floodOpacity: 'flood-opacity',
fontFamily: 'font-family',
fontSize: 'font-size',
fontSizeAdjust: 'font-size-adjust',
fontStretch: 'font-stretch',
fontStyle: 'font-style',
fontVariant: 'font-variant',
fontWeight: 'font-weight',
glyphName: 'glyph-name',
glyphOrientationHorizontal: 'glyph-orientation-horizontal',
glyphOrientationVertical: 'glyph-orientation-vertical',
hrefLang: 'hreflang',
horizAdvX: 'horiz-adv-x',
horizOriginX: 'horiz-origin-x',
horizOriginY: 'horiz-origin-y',
imageRendering: 'image-rendering',
letterSpacing: 'letter-spacing',
lightingColor: 'lighting-color',
markerEnd: 'marker-end',
markerMid: 'marker-mid',
markerStart: 'marker-start',
navDown: 'nav-down',
navDownLeft: 'nav-down-left',
navDownRight: 'nav-down-right',
navLeft: 'nav-left',
navNext: 'nav-next',
navPrev: 'nav-prev',
navRight: 'nav-right',
navUp: 'nav-up',
navUpLeft: 'nav-up-left',
navUpRight: 'nav-up-right',
onAbort: 'onabort',
onActivate: 'onactivate',
onAfterPrint: 'onafterprint',
onBeforePrint: 'onbeforeprint',
onBegin: 'onbegin',
onCancel: 'oncancel',
onCanPlay: 'oncanplay',
onCanPlayThrough: 'oncanplaythrough',
onChange: 'onchange',
onClick: 'onclick',
onClose: 'onclose',
onCopy: 'oncopy',
onCueChange: 'oncuechange',
onCut: 'oncut',
onDblClick: 'ondblclick',
onDrag: 'ondrag',
onDragEnd: 'ondragend',
onDragEnter: 'ondragenter',
onDragExit: 'ondragexit',
onDragLeave: 'ondragleave',
onDragOver: 'ondragover',
onDragStart: 'ondragstart',
onDrop: 'ondrop',
onDurationChange: 'ondurationchange',
onEmptied: 'onemptied',
onEnd: 'onend',
onEnded: 'onended',
onError: 'onerror',
onFocus: 'onfocus',
onFocusIn: 'onfocusin',
onFocusOut: 'onfocusout',
onHashChange: 'onhashchange',
onInput: 'oninput',
onInvalid: 'oninvalid',
onKeyDown: 'onkeydown',
onKeyPress: 'onkeypress',
onKeyUp: 'onkeyup',
onLoad: 'onload',
onLoadedData: 'onloadeddata',
onLoadedMetadata: 'onloadedmetadata',
onLoadStart: 'onloadstart',
onMessage: 'onmessage',
onMouseDown: 'onmousedown',
onMouseEnter: 'onmouseenter',
onMouseLeave: 'onmouseleave',
onMouseMove: 'onmousemove',
onMouseOut: 'onmouseout',
onMouseOver: 'onmouseover',
onMouseUp: 'onmouseup',
onMouseWheel: 'onmousewheel',
onOffline: 'onoffline',
onOnline: 'ononline',
onPageHide: 'onpagehide',
onPageShow: 'onpageshow',
onPaste: 'onpaste',
onPause: 'onpause',
onPlay: 'onplay',
onPlaying: 'onplaying',
onPopState: 'onpopstate',
onProgress: 'onprogress',
onRateChange: 'onratechange',
onRepeat: 'onrepeat',
onReset: 'onreset',
onResize: 'onresize',
onScroll: 'onscroll',
onSeeked: 'onseeked',
onSeeking: 'onseeking',
onSelect: 'onselect',
onShow: 'onshow',
onStalled: 'onstalled',
onStorage: 'onstorage',
onSubmit: 'onsubmit',
onSuspend: 'onsuspend',
onTimeUpdate: 'ontimeupdate',
onToggle: 'ontoggle',
onUnload: 'onunload',
onVolumeChange: 'onvolumechange',
onWaiting: 'onwaiting',
onZoom: 'onzoom',
overlinePosition: 'overline-position',
overlineThickness: 'overline-thickness',
paintOrder: 'paint-order',
panose1: 'panose-1',
pointerEvents: 'pointer-events',
referrerPolicy: 'referrerpolicy',
renderingIntent: 'rendering-intent',
shapeRendering: 'shape-rendering',
stopColor: 'stop-color',
stopOpacity: 'stop-opacity',
strikethroughPosition: 'strikethrough-position',
strikethroughThickness: 'strikethrough-thickness',
strokeDashArray: 'stroke-dasharray',
strokeDashOffset: 'stroke-dashoffset',
strokeLineCap: 'stroke-linecap',
strokeLineJoin: 'stroke-linejoin',
strokeMiterLimit: 'stroke-miterlimit',
strokeOpacity: 'stroke-opacity',
strokeWidth: 'stroke-width',
tabIndex: 'tabindex',
textAnchor: 'text-anchor',
textDecoration: 'text-decoration',
textRendering: 'text-rendering',
typeOf: 'typeof',
underlinePosition: 'underline-position',
underlineThickness: 'underline-thickness',
unicodeBidi: 'unicode-bidi',
unicodeRange: 'unicode-range',
unitsPerEm: 'units-per-em',
vAlphabetic: 'v-alphabetic',
vHanging: 'v-hanging',
vIdeographic: 'v-ideographic',
vMathematical: 'v-mathematical',
vectorEffect: 'vector-effect',
vertAdvY: 'vert-adv-y',
vertOriginX: 'vert-origin-x',
vertOriginY: 'vert-origin-y',
wordSpacing: 'word-spacing',
writingMode: 'writing-mode',
xHeight: 'x-height',
// These were camelcased in Tiny. Now lowercased in SVG 2
playbackOrder: 'playbackorder',
timelineBegin: 'timelinebegin'
},
transform: caseSensitiveTransform,
properties: {
about: commaOrSpaceSeparated,
accentHeight: number,
accumulate: null,
additive: null,
alignmentBaseline: null,
alphabetic: number,
amplitude: number,
arabicForm: null,
ascent: number,
attributeName: null,
attributeType: null,
azimuth: number,
bandwidth: null,
baselineShift: null,
baseFrequency: null,
baseProfile: null,
bbox: null,
begin: null,
bias: number,
by: null,
calcMode: null,
capHeight: number,
className: spaceSeparated,
clip: null,
clipPath: null,
clipPathUnits: null,
clipRule: null,
color: null,
colorInterpolation: null,
colorInterpolationFilters: null,
colorProfile: null,
colorRendering: null,
content: null,
contentScriptType: null,
contentStyleType: null,
crossOrigin: null,
cursor: null,
cx: null,
cy: null,
d: null,
dataType: null,
defaultAction: null,
descent: number,
diffuseConstant: number,
direction: null,
display: null,
dur: null,
divisor: number,
dominantBaseline: null,
download: boolean,
dx: null,
dy: null,
edgeMode: null,
editable: null,
elevation: number,
enableBackground: null,
end: null,
event: null,
exponent: number,
externalResourcesRequired: null,
fill: null,
fillOpacity: number,
fillRule: null,
filter: null,
filterRes: null,
filterUnits: null,
floodColor: null,
floodOpacity: null,
focusable: null,
focusHighlight: null,
fontFamily: null,
fontSize: null,
fontSizeAdjust: null,
fontStretch: null,
fontStyle: null,
fontVariant: null,
fontWeight: null,
format: null,
fr: null,
from: null,
fx: null,
fy: null,
g1: commaSeparated,
g2: commaSeparated,
glyphName: commaSeparated,
glyphOrientationHorizontal: null,
glyphOrientationVertical: null,
glyphRef: null,
gradientTransform: null,
gradientUnits: null,
handler: null,
hanging: number,
hatchContentUnits: null,
hatchUnits: null,
height: null,
href: null,
hrefLang: null,
horizAdvX: number,
horizOriginX: number,
horizOriginY: number,
id: null,
ideographic: number,
imageRendering: null,
initialVisibility: null,
in: null,
in2: null,
intercept: number,
k: number,
k1: number,
k2: number,
k3: number,
k4: number,
kernelMatrix: commaOrSpaceSeparated,
kernelUnitLength: null,
keyPoints: null, // SEMI_COLON_SEPARATED
keySplines: null, // SEMI_COLON_SEPARATED
keyTimes: null, // SEMI_COLON_SEPARATED
kerning: null,
lang: null,
lengthAdjust: null,
letterSpacing: null,
lightingColor: null,
limitingConeAngle: number,
local: null,
markerEnd: null,
markerMid: null,
markerStart: null,
markerHeight: null,
markerUnits: null,
markerWidth: null,
mask: null,
maskContentUnits: null,
maskUnits: null,
mathematical: null,
max: null,
media: null,
mediaCharacterEncoding: null,
mediaContentEncodings: null,
mediaSize: number,
mediaTime: null,
method: null,
min: null,
mode: null,
name: null,
navDown: null,
navDownLeft: null,
navDownRight: null,
navLeft: null,
navNext: null,
navPrev: null,
navRight: null,
navUp: null,
navUpLeft: null,
navUpRight: null,
numOctaves: null,
observer: null,
offset: null,
onAbort: null,
onActivate: null,
onAfterPrint: null,
onBeforePrint: null,
onBegin: null,
onCancel: null,
onCanPlay: null,
onCanPlayThrough: null,
onChange: null,
onClick: null,
onClose: null,
onCopy: null,
onCueChange: null,
onCut: null,
onDblClick: null,
onDrag: null,
onDragEnd: null,
onDragEnter: null,
onDragExit: null,
onDragLeave: null,
onDragOver: null,
onDragStart: null,
onDrop: null,
onDurationChange: null,
onEmptied: null,
onEnd: null,
onEnded: null,
onError: null,
onFocus: null,
onFocusIn: null,
onFocusOut: null,
onHashChange: null,
onInput: null,
onInvalid: null,
onKeyDown: null,
onKeyPress: null,
onKeyUp: null,
onLoad: null,
onLoadedData: null,
onLoadedMetadata: null,
onLoadStart: null,
onMessage: null,
onMouseDown: null,
onMouseEnter: null,
onMouseLeave: null,
onMouseMove: null,
onMouseOut: null,
onMouseOver: null,
onMouseUp: null,
onMouseWheel: null,
onOffline: null,
onOnline: null,
onPageHide: null,
onPageShow: null,
onPaste: null,
onPause: null,
onPlay: null,
onPlaying: null,
onPopState: null,
onProgress: null,
onRateChange: null,
onRepeat: null,
onReset: null,
onResize: null,
onScroll: null,
onSeeked: null,
onSeeking: null,
onSelect: null,
onShow: null,
onStalled: null,
onStorage: null,
onSubmit: null,
onSuspend: null,
onTimeUpdate: null,
onToggle: null,
onUnload: null,
onVolumeChange: null,
onWaiting: null,
onZoom: null,
opacity: null,
operator: null,
order: null,
orient: null,
orientation: null,
origin: null,
overflow: null,
overlay: null,
overlinePosition: number,
overlineThickness: number,
paintOrder: null,
panose1: null,
path: null,
pathLength: number,
patternContentUnits: null,
patternTransform: null,
patternUnits: null,
phase: null,
ping: spaceSeparated,
pitch: null,
playbackOrder: null,
pointerEvents: null,
points: null,
pointsAtX: number,
pointsAtY: number,
pointsAtZ: number,
preserveAlpha: null,
preserveAspectRatio: null,
primitiveUnits: null,
propagate: null,
property: commaOrSpaceSeparated,
r: null,
radius: null,
referrerPolicy: null,
refX: null,
refY: null,
rel: commaOrSpaceSeparated,
rev: commaOrSpaceSeparated,
renderingIntent: null,
repeatCount: null,
repeatDur: null,
requiredExtensions: commaOrSpaceSeparated,
requiredFeatures: commaOrSpaceSeparated,
requiredFonts: commaOrSpaceSeparated,
requiredFormats: commaOrSpaceSeparated,
resource: null,
restart: null,
result: null,
rotate: null,
rx: null,
ry: null,
scale: null,
seed: null,
shapeRendering: null,
side: null,
slope: null,
snapshotTime: null,
specularConstant: number,
specularExponent: number,
spreadMethod: null,
spacing: null,
startOffset: null,
stdDeviation: null,
stemh: null,
stemv: null,
stitchTiles: null,
stopColor: null,
stopOpacity: null,
strikethroughPosition: number,
strikethroughThickness: number,
string: null,
stroke: null,
strokeDashArray: commaOrSpaceSeparated,
strokeDashOffset: null,
strokeLineCap: null,
strokeLineJoin: null,
strokeMiterLimit: number,
strokeOpacity: number,
strokeWidth: null,
style: null,
surfaceScale: number,
syncBehavior: null,
syncBehaviorDefault: null,
syncMaster: null,
syncTolerance: null,
syncToleranceDefault: null,
systemLanguage: commaOrSpaceSeparated,
tabIndex: number,
tableValues: null,
target: null,
targetX: number,
targetY: number,
textAnchor: null,
textDecoration: null,
textRendering: null,
textLength: null,
timelineBegin: null,
title: null,
transformBehavior: null,
type: null,
typeOf: commaOrSpaceSeparated,
to: null,
transform: null,
u1: null,
u2: null,
underlinePosition: number,
underlineThickness: number,
unicode: null,
unicodeBidi: null,
unicodeRange: null,
unitsPerEm: number,
values: null,
vAlphabetic: number,
vMathematical: number,
vectorEffect: null,
vHanging: number,
vIdeographic: number,
version: null,
vertAdvY: number,
vertOriginX: number,
vertOriginY: number,
viewBox: null,
viewTarget: null,
visibility: null,
width: null,
widths: null,
wordSpacing: null,
writingMode: null,
x: null,
x1: null,
x2: null,
xChannelSelector: null,
xHeight: number,
y: null,
y1: null,
y2: null,
yChannelSelector: null,
z: null,
zoomAndPan: null
}
});
/**
* @typedef {import('./util/schema.js').Schema} Schema
*/
const valid = /^data[-\w.:]+$/i;
const dash = /-[a-z]/g;
const cap = /[A-Z]/g;
/**
* @param {Schema} schema
* @param {string} value
* @returns {Info}
*/
function find(schema, value) {
const normal = normalize(value);
let prop = value;
let Type = Info;
if (normal in schema.normal) {
return schema.property[schema.normal[normal]]
}
if (normal.length > 4 && normal.slice(0, 4) === 'data' && valid.test(value)) {
// Attribute or property.
if (value.charAt(4) === '-') {
// Turn it into a property.
const rest = value.slice(5).replace(dash, camelcase);
prop = 'data' + rest.charAt(0).toUpperCase() + rest.slice(1);
} else {
// Turn it into an attribute.
const rest = value.slice(4);
if (!dash.test(rest)) {
let dashes = rest.replace(cap, kebab);
if (dashes.charAt(0) !== '-') {
dashes = '-' + dashes;
}
value = 'data' + dashes;
}
}
Type = DefinedInfo;
}
return new Type(prop, value)
}
/**
* @param {string} $0
* @returns {string}
*/
function kebab($0) {
return '-' + $0.toLowerCase()
}
/**
* @param {string} $0
* @returns {string}
*/
function camelcase($0) {
return $0.charAt(1).toUpperCase()
}
/**
* @typedef {import('./lib/util/info.js').Info} Info
* @typedef {import('./lib/util/schema.js').Schema} Schema
*/
const html = merge([xml, xlink, xmlns, aria, html$1], 'html');
const svg = merge([xml, xlink, xmlns, aria, svg$1], 'svg');
var own$2 = {}.hasOwnProperty;
/**
* @callback Handler
* @param {...unknown} value
* @return {unknown}
*
* @typedef {Record<string, Handler>} Handlers
*
* @typedef {Object} Options
* @property {Handler} [unknown]
* @property {Handler} [invalid]
* @property {Handlers} [handlers]
*/
/**
* Handle values based on a property.
*
* @param {string} key
* @param {Options} [options]
*/
function zwitch(key, options) {
var settings = options || {};
/**
* Handle one value.
* Based on the bound `key`, a respective handler will be called.
* If `value` is not an object, or doesn’t have a `key` property, the special
* “invalid” handler will be called.
* If `value` has an unknown `key`, the special “unknown” handler will be
* called.
*
* All arguments, and the context object, are passed through to the handler,
* and it’s result is returned.
*
* @param {...unknown} [value]
* @this {unknown}
* @returns {unknown}
* @property {Handler} invalid
* @property {Handler} unknown
* @property {Handlers} handlers
*/
function one(value) {
var fn = one.invalid;
var handlers = one.handlers;
if (value && own$2.call(value, key)) {
fn = own$2.call(handlers, value[key]) ? handlers[value[key]] : one.unknown;
}
if (fn) {
return fn.apply(this, arguments)
}
}
one.handlers = settings.handlers || {};
one.invalid = settings.invalid;
one.unknown = settings.unknown;
return one
}
const rtlRange = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
const ltrRange =
'A-Za-z\u00C0-\u00D6\u00D8-\u00F6' +
'\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u200E\u2C00-\uFB1C' +
'\uFE00-\uFE6F\uFEFD-\uFFFF';
/* eslint-disable no-misleading-character-class */
const rtl = new RegExp('^[^' + ltrRange + ']*[' + rtlRange + ']');
const ltr = new RegExp('^[^' + rtlRange + ']*[' + ltrRange + ']');
/* eslint-enable no-misleading-character-class */
/**
* Detect the direction of text: left-to-right, right-to-left, or neutral
*
* @param {string} value
* @returns {'rtl'|'ltr'|'neutral'}
*/
function direction(value) {
const source = String(value || '');
return rtl.test(source) ? 'rtl' : ltr.test(source) ? 'ltr' : 'neutral'
}
/**
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('hast').Element} Element
*
* @typedef {string} TagName
* @typedef {null|undefined|TagName|TestFunctionAnything|Array.<TagName|TestFunctionAnything>} Test
*/
/**
* @template {Element} T
* @typedef {null|undefined|T['tagName']|TestFunctionPredicate<T>|Array.<T['tagName']|TestFunctionPredicate<T>>} PredicateTest
*/
/**
* Check if an element passes a test
*
* @callback TestFunctionAnything
* @param {Element} element
* @param {number|null|undefined} [index]
* @param {Parent|null|undefined} [parent]
* @returns {boolean|void}
*/
/**
* Check if an element passes a certain node test
*
* @template {Element} X
* @callback TestFunctionPredicate
* @param {Element} element
* @param {number|null|undefined} [index]
* @param {Parent|null|undefined} [parent]
* @returns {element is X}
*/
/**
* Check if a node is an element and passes a certain node test
*
* @callback AssertAnything
* @param {unknown} [node]
* @param {number|null|undefined} [index]
* @param {Parent|null|undefined} [parent]
* @returns {boolean}
*/
/**
* Check if a node is an element and passes a certain node test
*
* @template {Element} Y
* @callback AssertPredicate
* @param {unknown} [node]
* @param {number|null|undefined} [index]
* @param {Parent|null|undefined} [parent]
* @returns {node is Y}
*/
// Check if `node` is an `element` and whether it passes the given test.
const isElement =
/**
* Check if a node is an element and passes a test.
* When a `parent` node is known the `index` of node should also be given.
*
* @type {(
* (() => false) &
* (<T extends Element = Element>(node: unknown, test?: PredicateTest<T>, index?: number, parent?: Parent, context?: unknown) => node is T) &
* ((node: unknown, test: Test, index?: number, parent?: Parent, context?: unknown) => boolean)
* )}
*/
(
/**
* Check if a node passes a test.
* When a `parent` node is known the `index` of node should also be given.
*
* @param {unknown} [node] Node to check
* @param {Test} [test] When nullish, checks if `node` is a `Node`.
* When `string`, works like passing `function (node) {return node.type === test}`.
* When `function` checks if function passed the node is true.
* When `array`, checks any one of the subtests pass.
* @param {number} [index] Position of `node` in `parent`
* @param {Parent} [parent] Parent of `node`
* @param {unknown} [context] Context object to invoke `test` with
* @returns {boolean} Whether test passed and `node` is an `Element` (object with `type` set to `element` and `tagName` set to a non-empty string).
*/
// eslint-disable-next-line max-params
function (node, test, index, parent, context) {
const check = convertElement(test);
if (
index !== undefined &&
index !== null &&
(typeof index !== 'number' ||
index < 0 ||
index === Number.POSITIVE_INFINITY)
) {
throw new Error('Expected positive finite index for child node')
}
if (
parent !== undefined &&
parent !== null &&
(!parent.type || !parent.children)
) {
throw new Error('Expected parent node')
}
// @ts-expect-error Looks like a node.
if (!node || !node.type || typeof node.type !== 'string') {
return false
}
if (
(parent === undefined || parent === null) !==
(index === undefined || index === null)
) {
throw new Error('Expected both parent and index')
}
return check.call(context, node, index, parent)
}
);
const convertElement =
/**
* @type {(
* (<T extends Element>(test: T['tagName']|TestFunctionPredicate<T>) => AssertPredicate<T>) &
* ((test?: Test) => AssertAnything)
* )}
*/
(
/**
* Generate an assertion from a check.
* @param {Test} [test]
* When nullish, checks if `node` is a `Node`.
* When `string`, works like passing `function (node) {return node.type === test}`.
* When `function` checks if function passed the node is true.
* When `object`, checks that all keys in test are in node, and that they have (strictly) equal values.
* When `array`, checks any one of the subtests pass.
* @returns {AssertAnything}
*/
function (test) {
if (test === undefined || test === null) {
return element$1
}
if (typeof test === 'string') {
return tagNameFactory(test)
}
if (typeof test === 'object') {
return anyFactory$1(test)
}
if (typeof test === 'function') {
return castFactory$1(test)
}
throw new Error('Expected function, string, or array as test')
}
);
/**
* @param {Array.<TagName|TestFunctionAnything>} tests
* @returns {AssertAnything}
*/
function anyFactory$1(tests) {
/** @type {Array.<AssertAnything>} */
const checks = [];
let index = -1;
while (++index < tests.length) {
checks[index] = convertElement(tests[index]);
}
return castFactory$1(any)
/**
* @this {unknown}
* @param {unknown[]} parameters
* @returns {boolean}
*/
function any(...parameters) {
let index = -1;
while (++index < checks.length) {
if (checks[index].call(this, ...parameters)) {
return true
}
}
return false
}
}
/**
* Utility to convert a string into a function which checks a given node’s tag
* name for said string.
*
* @param {TagName} check
* @returns {AssertAnything}
*/
function tagNameFactory(check) {
return tagName
/**
* @param {unknown} node
* @returns {boolean}
*/
function tagName(node) {
return element$1(node) && node.tagName === check
}
}
/**
* @param {TestFunctionAnything} check
* @returns {AssertAnything}
*/
function castFactory$1(check) {
return assertion
/**
* @this {unknown}
* @param {unknown} node
* @param {Array.<unknown>} parameters
* @returns {boolean}
*/
function assertion(node, ...parameters) {
// @ts-expect-error: fine.
return element$1(node) && Boolean(check.call(this, node, ...parameters))
}
}
/**
* Utility to return true if this is an element.
* @param {unknown} node
* @returns {node is Element}
*/
function element$1(node) {
return Boolean(
node &&
typeof node === 'object' &&
// @ts-expect-error Looks like a node.
node.type === 'element' &&
// @ts-expect-error Looks like an element.
typeof node.tagName === 'string'
)
}
/**
* @fileoverview
* Get the plain-text value of a hast node.
* @longdescription
* ## Use
*
* ```js
* import {h} from 'hastscript'
* import {toString} from 'hast-util-to-string'
*
* toString(h('p', 'Alpha'))
* //=> 'Alpha'
* toString(h('div', [h('b', 'Bold'), ' and ', h('i', 'italic'), '.']))
* //=> 'Bold and italic.'
* ```
*
* ## API
*
* ### `toString(node)`
*
* Transform a node to a string.
*/
/**
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Element} Element
* @typedef {Root|Root['children'][number]} Node
*/
/**
* Get the plain-text value of a hast node.
*
* @param {Node} node
* @returns {string}
*/
function toString(node) {
// “The concatenation of data of all the Text node descendants of the context
// object, in tree order.”
if ('children' in node) {
return all(node)
}
// “Context object’s data.”
return 'value' in node ? node.value : ''
}
/**
* @param {Node} node
* @returns {string}
*/
function one(node) {
if (node.type === 'text') {
return node.value
}
return 'children' in node ? all(node) : ''
}
/**
* @param {Root|Element} node
* @returns {string}
*/
function all(node) {
let index = -1;
/** @type {string[]} */
const result = [];
while (++index < node.children.length) {
result[index] = one(node.children[index]);
}
return result.join('')
}
/**
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
*
* @typedef {string} Type
* @typedef {Object<string, unknown>} Props
*
* @typedef {null|undefined|Type|Props|TestFunctionAnything|Array.<Type|Props|TestFunctionAnything>} Test
*/
const convert =
/**
* @type {(
* (<T extends Node>(test: T['type']|Partial<T>|TestFunctionPredicate<T>) => AssertPredicate<T>) &
* ((test?: Test) => AssertAnything)
* )}
*/
(
/**
* Generate an assertion from a check.
* @param {Test} [test]
* When nullish, checks if `node` is a `Node`.
* When `string`, works like passing `function (node) {return node.type === test}`.
* When `function` checks if function passed the node is true.
* When `object`, checks that all keys in test are in node, and that they have (strictly) equal values.
* When `array`, checks any one of the subtests pass.
* @returns {AssertAnything}
*/
function (test) {
if (test === undefined || test === null) {
return ok
}
if (typeof test === 'string') {
return typeFactory(test)
}
if (typeof test === 'object') {
return Array.isArray(test) ? anyFactory(test) : propsFactory(test)
}
if (typeof test === 'function') {
return castFactory(test)
}
throw new Error('Expected function, string, or object as test')
}
);
/**
* @param {Array.<Type|Props|TestFunctionAnything>} tests
* @returns {AssertAnything}
*/
function anyFactory(tests) {
/** @type {Array.<AssertAnything>} */
const checks = [];
let index = -1;
while (++index < tests.length) {
checks[index] = convert(tests[index]);
}
return castFactory(any)
/**
* @this {unknown}
* @param {unknown[]} parameters
* @returns {boolean}
*/
function any(...parameters) {
let index = -1;
while (++index < checks.length) {
if (checks[index].call(this, ...parameters)) return true
}
return false
}
}
/**
* Utility to assert each property in `test` is represented in `node`, and each
* values are strictly equal.
*
* @param {Props} check
* @returns {AssertAnything}
*/
function propsFactory(check) {
return castFactory(all)
/**
* @param {Node} node
* @returns {boolean}
*/
function all(node) {
/** @type {string} */
let key;
for (key in check) {
// @ts-expect-error: hush, it sure works as an index.
if (node[key] !== check[key]) return false
}
return true
}
}
/**
* Utility to convert a string into a function which checks a given node’s type
* for said string.
*
* @param {Type} check
* @returns {AssertAnything}
*/
function typeFactory(check) {
return castFactory(type)
/**
* @param {Node} node
*/
function type(node) {
return node && node.type === check
}
}
/**
* Utility to convert a string into a function which checks a given node’s type
* for said string.
* @param {TestFunctionAnything} check
* @returns {AssertAnything}
*/
function castFactory(check) {
return assertion
/**
* @this {unknown}
* @param {Array.<unknown>} parameters
* @returns {boolean}
*/
function assertion(...parameters) {
// @ts-expect-error: spreading is fine.
return Boolean(check.call(this, ...parameters))
}
}
// Utility to return true.
function ok() {
return true
}
/**
* @param {string} d
* @returns {string}
*/
function color(d) {
return '\u001B[33m' + d + '\u001B[39m'
}
/**
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('unist-util-is').Test} Test
* @typedef {import('./complex-types').Action} Action
* @typedef {import('./complex-types').Index} Index
* @typedef {import('./complex-types').ActionTuple} ActionTuple
* @typedef {import('./complex-types').VisitorResult} VisitorResult
* @typedef {import('./complex-types').Visitor} Visitor
*/
/**
* Continue traversing as normal
*/
const CONTINUE = true;
/**
* Do not traverse this node’s children
*/
const SKIP = 'skip';
/**
* Stop traversing immediately
*/
const EXIT = false;
/**
* Visit children of tree which pass a test
*
* @param tree Abstract syntax tree to walk
* @param test Test node, optional
* @param visitor Function to run for each node
* @param reverse Visit the tree in reverse order, defaults to false
*/
const visitParents =
/**
* @type {(
* (<Tree extends Node, Check extends Test>(tree: Tree, test: Check, visitor: import('./complex-types').BuildVisitor<Tree, Check>, reverse?: boolean) => void) &
* (<Tree extends Node>(tree: Tree, visitor: import('./complex-types').BuildVisitor<Tree>, reverse?: boolean) => void)
* )}
*/
(
/**
* @param {Node} tree
* @param {Test} test
* @param {import('./complex-types').Visitor<Node>} visitor
* @param {boolean} [reverse]
*/
function (tree, test, visitor, reverse) {
if (typeof test === 'function' && typeof visitor !== 'function') {
reverse = visitor;
// @ts-expect-error no visitor given, so `visitor` is test.
visitor = test;
test = null;
}
const is = convert(test);
const step = reverse ? -1 : 1;
factory(tree, null, [])();
/**
* @param {Node} node
* @param {number?} index
* @param {Array.<Parent>} parents
*/
function factory(node, index, parents) {
/** @type {Object.<string, unknown>} */
// @ts-expect-error: hush
const value = typeof node === 'object' && node !== null ? node : {};
/** @type {string|undefined} */
let name;
if (typeof value.type === 'string') {
name =
typeof value.tagName === 'string'
? value.tagName
: typeof value.name === 'string'
? value.name
: undefined;
Object.defineProperty(visit, 'name', {
value:
'node (' +
color(value.type + (name ? '<' + name + '>' : '')) +
')'
});
}
return visit
function visit() {
/** @type {ActionTuple} */
let result = [];
/** @type {ActionTuple} */
let subresult;
/** @type {number} */
let offset;
/** @type {Array.<Parent>} */
let grandparents;
if (!test || is(node, index, parents[parents.length - 1] || null)) {
result = toResult(visitor(node, parents));
if (result[0] === EXIT) {
return result
}
}
// @ts-expect-error looks like a parent.
if (node.children && result[0] !== SKIP) {
// @ts-expect-error looks like a parent.
offset = (reverse ? node.children.length : -1) + step;
// @ts-expect-error looks like a parent.
grandparents = parents.concat(node);
// @ts-expect-error looks like a parent.
while (offset > -1 && offset < node.children.length) {
// @ts-expect-error looks like a parent.
subresult = factory(node.children[offset], offset, grandparents)();
if (subresult[0] === EXIT) {
return subresult
}
offset =
typeof subresult[1] === 'number' ? subresult[1] : offset + step;
}
}
return result
}
}
}
);
/**
* @param {VisitorResult} value
* @returns {ActionTuple}
*/
function toResult(value) {
if (Array.isArray(value)) {
return value
}
if (typeof value === 'number') {
return [CONTINUE, value]
}
return [value]
}
/**
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('unist-util-is').Test} Test
* @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult
* @typedef {import('./complex-types').Visitor} Visitor
*/
/**
* Visit children of tree which pass a test
*
* @param tree Abstract syntax tree to walk
* @param test Test, optional
* @param visitor Function to run for each node
* @param reverse Fisit the tree in reverse, defaults to false
*/
const visit =
/**
* @type {(
* (<Tree extends Node, Check extends Test>(tree: Tree, test: Check, visitor: import('./complex-types').BuildVisitor<Tree, Check>, reverse?: boolean) => void) &
* (<Tree extends Node>(tree: Tree, visitor: import('./complex-types').BuildVisitor<Tree>, reverse?: boolean) => void)
* )}
*/
(
/**
* @param {Nod