diff --git a/src/index.js b/src/index.js index c648b4f..8dcffee 100644 --- a/src/index.js +++ b/src/index.js @@ -5,7 +5,7 @@ import { NAMESPACE_REPLACE_REGEX, HTML_LOWER_CASE, HTML_ENUMERATED, - SVG_CAMEL_CASE, + SVG_KEBAB_CASE, createComponent } from './lib/util.js'; import { options, h, Fragment } from 'preact'; @@ -631,13 +631,13 @@ function _renderToString( // serialize boolean aria-xyz or enumerated attribute values as strings v = v + EMPTY_STR; } else if (isSvgMode) { - if (SVG_CAMEL_CASE.test(name)) { + if (SVG_KEBAB_CASE.has(name)) { name = name === 'panose1' ? 'panose-1' : name.replace(/([A-Z])/g, '-$1').toLowerCase(); } - } else if (HTML_LOWER_CASE.test(name)) { + } else if (HTML_LOWER_CASE.has(name)) { name = name.toLowerCase(); } } diff --git a/src/lib/util.js b/src/lib/util.js index 84b348d..3ad443a 100644 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -1,8 +1,144 @@ -export const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/; +export const VOID_ELEMENTS = new Set([ + 'area', + 'base', + 'br', + 'col', + 'command', + 'embed', + 'hr', + 'img', + 'input', + 'keygen', + 'link', + 'meta', + 'param', + 'source', + 'track', + 'wbr' +]); export const UNSAFE_NAME = /[\s\n\\/='"\0<>]/; export const NAMESPACE_REPLACE_REGEX = /^(xlink|xmlns|xml)([A-Z])/; -export const HTML_LOWER_CASE = /^accessK|^auto[A-Z]|^cell|^ch|^col|cont|cross|dateT|encT|form[A-Z]|frame|hrefL|inputM|maxL|minL|noV|playsI|popoverT|readO|rowS|src[A-Z]|tabI|useM|item[A-Z]/; -export const SVG_CAMEL_CASE = /^ac|^ali|arabic|basel|cap|clipPath$|clipRule$|color|dominant|enable|fill|flood|font|glyph[^R]|horiz|image|letter|lighting|marker[^WUH]|overline|panose|pointe|paint|rendering|shape|stop|strikethrough|stroke|text[^L]|transform|underline|unicode|units|^v[^i]|^w|^xH/; +export const HTML_LOWER_CASE = new Set([ + 'accessKey', + 'accessKeyLabel', + 'autoComplete', + 'autoCorrect', + 'autoFocus', + 'autoPlay', + 'autoCapitalize', + 'cellPadding', + 'cellSpacing', + 'charSet', + 'colSpan', + 'contentEditable', + 'contextMenu', + 'controlsList', + 'crossOrigin', + 'dateTime', + 'encType', + 'formAction', + 'formEncType', + 'formMethod', + 'formNoValidate', + 'formTarget', + 'frameBorder', + 'hrefLang', + 'inputMode', + 'maxLength', + 'minLength', + 'noValidate', + 'playsInline', + 'popoverTarget', + 'popoverTargetAction', + 'readOnly', + 'rowSpan', + 'srcSet', + 'srcDoc', + 'srcLang', + 'tabIndex', + 'useMap', + 'itemProp', + 'itemScope', + 'itemType', + 'itemID', + 'itemRef' +]); +export const SVG_KEBAB_CASE = new Set([ + 'accentHeight', + 'accumulate', + 'alignmentBaseline', + 'arabicForm', + 'baselineShift', + 'capHeight', + 'clipPath', + 'clipRule', + 'colorInterpolation', + 'colorInterpolationFilters', + 'colorProfile', + 'colorRendering', + 'dominantBaseline', + 'enableBackground', + 'fillOpacity', + 'fillRule', + 'floodColor', + 'floodOpacity', + 'fontFamily', + 'fontSize', + 'fontSizeAdjust', + 'fontStretch', + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'glyphName', + 'glyphOrientationHorizontal', + 'glyphOrientationVertical', + 'horizAdvX', + 'horizOriginX', + 'imageRendering', + 'letterSpacing', + 'lightingColor', + 'markerEnd', + 'markerMid', + 'markerStart', + 'overlinePosition', + 'overlineThickness', + 'paintOrder', + 'panose1', + 'pointerEvents', + 'renderingIntent', + 'shapeRendering', + 'stopColor', + 'stopOpacity', + 'strikethroughPosition', + 'strikethroughThickness', + 'strokeDasharray', + 'strokeDashoffset', + 'strokeLinecap', + 'strokeLinejoin', + 'strokeMiterlimit', + 'strokeOpacity', + 'strokeWidth', + 'textAnchor', + 'textDecoration', + 'textRendering', + 'transformOrigin', + 'underlinePosition', + 'underlineThickness', + 'unicodeBidi', + 'unicodeRange', + 'unitsPerEm', + 'vAlphabetic', + 'vectorEffect', + 'vertAdvY', + 'vertOriginX', + 'vertOriginY', + 'vHanging', + 'vIdeographic', + 'vMathematical', + 'wordSpacing', + 'writingMode', + 'xHeight' +]); // Boolean DOM properties that translate to enumerated ('true'/'false') attributes export const HTML_ENUMERATED = new Set(['draggable', 'spellcheck']); diff --git a/src/pretty.js b/src/pretty.js index 59df0e4..3bd351d 100644 --- a/src/pretty.js +++ b/src/pretty.js @@ -8,7 +8,7 @@ import { UNSAFE_NAME, VOID_ELEMENTS, NAMESPACE_REPLACE_REGEX, - SVG_CAMEL_CASE, + SVG_KEBAB_CASE, HTML_LOWER_CASE, getContext } from './lib/util.js'; @@ -252,13 +252,13 @@ function _renderToStringPretty( } else if (NAMESPACE_REPLACE_REGEX.test(name)) { name = name.replace(NAMESPACE_REPLACE_REGEX, '$1:$2').toLowerCase(); } else if (isSvgMode) { - if (SVG_CAMEL_CASE.test(name)) { + if (SVG_KEBAB_CASE.has(name)) { name = name === 'panose1' ? 'panose-1' : name.replace(/([A-Z])/g, '-$1').toLowerCase(); } - } else if (HTML_LOWER_CASE.test(name)) { + } else if (HTML_LOWER_CASE.has(name)) { name = name.toLowerCase(); } @@ -332,7 +332,7 @@ function _renderToStringPretty( throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`); let isVoid = - VOID_ELEMENTS.test(nodeName) || + VOID_ELEMENTS.has(nodeName) || (opts.voidElements && opts.voidElements.test(nodeName)); let pieces = [];