4646 * @typedef {'html' | 'svg' } Space
4747 * Namespace.
4848 *
49- * @typedef {'react ' | 'html ' } ElementAttributeNameCase
50- * Specify casing to use for attribute names.
49+ * @typedef {'html ' | 'react ' } ElementAttributeNameCase
50+ * Casing to use for attribute names.
5151 *
52- * React casing is for example `className`, `strokeLinecap`, `xmlLang`.
5352 * HTML casing is for example `class`, `stroke-linecap`, `xml:lang`.
53+ * React casing is for example `className`, `strokeLinecap`, `xmlLang`.
54+ *
55+ * @typedef {'css' | 'dom' } StylePropertyNameCase
56+ * Casing to use for property names in `style` objects.
57+ *
58+ * CSS casing is for example `background-color` and `-webkit-line-clamp`.
59+ * DOM casing is for example `backgroundColor` and `WebkitLineClamp`.
5460 *
5561 * @typedef Source
5662 * Info about source.
99105 * Pass `node` to components.
100106 * @property {ElementAttributeNameCase } elementAttributeNameCase
101107 * Casing to use for attribute names.
108+ * @property {StylePropertyNameCase } stylePropertyNameCase
109+ * Casing to use for property names in `style` objects.
102110 * @property {Schema } schema
103111 * Current schema.
104112 * @property {unknown } Fragment
122130 * Pass the hast element node to components.
123131 * @property {ElementAttributeNameCase | null | undefined } [elementAttributeNameCase='react']
124132 * Specify casing to use for attribute names.
133+ * @property {StylePropertyNameCase | null | undefined } [stylePropertyNameCase='dom']
134+ * Specify casing to use for property names in `style` objects.
125135 * @property {Space | null | undefined } [space='html']
126136 * Whether `tree` is in the `'html'` or `'svg'` space.
127137 *
@@ -195,6 +205,9 @@ import {whitespace} from 'hast-util-whitespace'
195205
196206const own = { } . hasOwnProperty
197207
208+ const cap = / [ A - Z ] / g
209+ const dashSomething = / - ( [ a - z ] ) / g
210+
198211// `react-dom` triggers a warning for *any* white space in tables.
199212// To follow GFM, `mdast-util-to-hast` injects line endings between elements.
200213// Other tools might do so too, but they don’t do here, so we remove all of
@@ -255,6 +268,7 @@ export function toJsxRuntime(tree, options) {
255268 schema : options . space === 'svg' ? svg : html ,
256269 passNode : options . passNode || false ,
257270 elementAttributeNameCase : options . elementAttributeNameCase || 'react' ,
271+ stylePropertyNameCase : options . stylePropertyNameCase || 'dom' ,
258272 components : options . components || { } ,
259273 filePath,
260274 create
@@ -485,8 +499,15 @@ function createProperty(state, node, prop, value) {
485499 }
486500
487501 // React only accepts `style` as object.
488- if ( info . property === 'style' && typeof value === 'string' ) {
489- return [ 'style' , parseStyle ( state , node , value ) ]
502+ if ( info . property === 'style' ) {
503+ let styleObject =
504+ typeof value === 'object' ? value : parseStyle ( state , node , String ( value ) )
505+
506+ if ( state . stylePropertyNameCase === 'css' ) {
507+ styleObject = transformStyleToCssCasing ( styleObject )
508+ }
509+
510+ return [ 'style' , styleObject ]
490511 }
491512
492513 return [
@@ -534,8 +555,8 @@ function parseStyle(state, node, value) {
534555 return result
535556
536557 /**
537- * Add a CSS property (normal, so with dashes) to `result` as a camelcased
538- * CSS property.
558+ * Add a CSS property (normal, so with dashes) to `result` as a DOM CSS
559+ * property.
539560 *
540561 * @param {string } name
541562 * Key.
@@ -545,9 +566,39 @@ function parseStyle(state, node, value) {
545566 * Nothing.
546567 */
547568 function replacer ( name , value ) {
548- if ( name . slice ( 0 , 4 ) === '-ms-' ) name = 'ms-' + name . slice ( 4 )
549- result [ name . replace ( / - ( [ a - z ] ) / g, replace ) ] = value
569+ let key = name
570+
571+ if ( key . slice ( 0 , 2 ) !== '--' ) {
572+ if ( key . slice ( 0 , 4 ) === '-ms-' ) key = 'ms-' + key . slice ( 4 )
573+ key = key . replace ( dashSomething , toCamel )
574+ }
575+
576+ result [ key ] = value
577+ }
578+ }
579+
580+ /**
581+ * Transform a DOM casing style object to a CSS casing style object.
582+ *
583+ * @param {Style } domCasing
584+ * @returns {Style }
585+ */
586+ function transformStyleToCssCasing ( domCasing ) {
587+ /** @type {Style } */
588+ const cssCasing = { }
589+ /** @type {string } */
590+ let from
591+
592+ for ( from in domCasing ) {
593+ if ( own . call ( domCasing , from ) ) {
594+ let to = from . replace ( cap , toDash )
595+ // Handle `ms-xxx` -> `-ms-xxx`.
596+ if ( to . slice ( 0 , 3 ) === 'ms-' ) to = '-' + to
597+ cssCasing [ to ] = domCasing [ from ]
598+ }
550599 }
600+
601+ return cssCasing
551602}
552603
553604/**
@@ -560,6 +611,18 @@ function parseStyle(state, node, value) {
560611 * @returns {string }
561612 * Capitalized `$1`.
562613 */
563- function replace ( _ , $1 ) {
614+ function toCamel ( _ , $1 ) {
564615 return $1 . toUpperCase ( )
565616}
617+
618+ /**
619+ * Make `$0` dash cased.
620+ *
621+ * @param {string } $0
622+ * Capitalized ASCII leter.
623+ * @returns {string }
624+ * Dash and lower letter.
625+ */
626+ function toDash ( $0 ) {
627+ return '-' + $0 . toLowerCase ( )
628+ }
0 commit comments