1- import htm from 'htm ' ;
1+ import { build , treeify } from '../../src/build.mjs ' ;
22
33/**
44 * @param {Babel } babel
@@ -16,8 +16,6 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
1616 const useNativeSpread = options . useNativeSpread ;
1717 const inlineVNodes = options . monomorphic || pragma === false ;
1818
19- const symbol = Symbol ( ) ;
20-
2119 function dottedIdentifier ( keypath ) {
2220 const path = keypath . split ( '.' ) ;
2321 let out ;
@@ -33,16 +31,32 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
3331 const end = parts . pop ( ) || '' ;
3432 return new RegExp ( parts . join ( '/' ) , end ) ;
3533 }
36-
37- function propertyValue ( valueOrNode ) {
38- return t . isNode ( valueOrNode ) ? valueOrNode : t . valueToNode ( valueOrNode ) ;
39- }
40-
34+
4135 function propertyName ( key ) {
42- if ( key . match ( / ( ^ \d | [ ^ a - z 0 - 9 _ $ ] ) / i) ) return t . stringLiteral ( key ) ;
43- return t . identifier ( key ) ;
36+ if ( t . isValidIdentifier ( key ) ) {
37+ return t . identifier ( key ) ;
38+ }
39+ return t . stringLiteral ( key ) ;
4440 }
45-
41+
42+ function objectProperties ( obj ) {
43+ return Object . keys ( obj ) . map ( key => {
44+ const values = obj [ key ] . map ( valueOrNode =>
45+ t . isNode ( valueOrNode ) ? valueOrNode : t . valueToNode ( valueOrNode )
46+ ) ;
47+
48+ let node = values [ 0 ] ;
49+ if ( values . length > 1 && ! t . isStringLiteral ( node ) && ! t . isStringLiteral ( values [ 1 ] ) ) {
50+ node = t . binaryExpression ( '+' , t . stringLiteral ( '' ) , node ) ;
51+ }
52+ values . slice ( 1 ) . forEach ( value => {
53+ node = t . binaryExpression ( '+' , node , value ) ;
54+ } ) ;
55+
56+ return t . objectProperty ( propertyName ( key ) , node ) ;
57+ } ) ;
58+ }
59+
4660 function stringValue ( str ) {
4761 if ( options . monomorphic ) {
4862 return t . objectExpression ( [
@@ -81,18 +95,10 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
8195 return t . callExpression ( pragma , [ tag , props ] . concat ( children ) ) ;
8296 }
8397
84- function flatten ( props , result = [ ] ) {
85- const { [ symbol ] : head , ...tail } = props ;
86- if ( head ) head . forEach ( obj => {
87- flatten ( obj , result ) ;
88- } ) ;
89- if ( Object . keys ( tail ) . length > 0 ) {
90- result . push ( tail ) ;
91- }
92- return result ;
93- }
94-
9598 function spreadNode ( args , state ) {
99+ if ( args . length === 0 ) {
100+ return t . nullLiteral ( ) ;
101+ }
96102 if ( args . length > 0 && t . isNode ( args [ 0 ] ) ) {
97103 args . unshift ( { } ) ;
98104 }
@@ -113,10 +119,7 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
113119 properties . push ( t . spreadElement ( arg ) ) ;
114120 }
115121 else {
116- Object . keys ( arg ) . forEach ( key => {
117- const value = arg [ key ] ;
118- properties . push ( t . objectProperty ( propertyName ( key ) , propertyValue ( value ) ) ) ;
119- } ) ;
122+ properties . push ( ...objectProperties ( arg ) ) ;
120123 }
121124 } ) ;
122125 return t . objectExpression ( properties ) ;
@@ -127,17 +130,12 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
127130 }
128131
129132 function propsNode ( props ) {
130- return t . isNode ( props ) ? props : t . objectExpression (
131- Object . keys ( props ) . map ( key => {
132- const value = props [ key ] ;
133- return t . objectProperty ( propertyName ( key ) , propertyValue ( value ) ) ;
134- } )
135- ) ;
133+ return t . isNode ( props ) ? props : t . objectExpression ( objectProperties ( props ) ) ;
136134 }
137135
138136 function transform ( node , state ) {
139137 if ( node === undefined ) return t . identifier ( 'undefined' ) ;
140- if ( node == null ) return t . nullLiteral ( ) ;
138+ if ( node === null ) return t . nullLiteral ( ) ;
141139
142140 const { tag, props, children } = node ;
143141 function childMapper ( child ) {
@@ -147,27 +145,10 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
147145 return t . isNode ( child ) ? child : transform ( child , state ) ;
148146 }
149147 const newTag = typeof tag === 'string' ? t . stringLiteral ( tag ) : tag ;
150- const newProps = props ? spreadNode ( flatten ( props ) , state ) : t . nullLiteral ( ) ;
148+ const newProps = spreadNode ( props , state ) ;
151149 const newChildren = t . arrayExpression ( children . map ( childMapper ) ) ;
152150 return createVNode ( newTag , newProps , newChildren ) ;
153151 }
154-
155- function h ( tag , props , ...children ) {
156- return { tag, props, children } ;
157- }
158-
159- const html = htm . bind ( h ) ;
160-
161- function treeify ( statics , expr ) {
162- const assign = Object . assign ;
163- try {
164- Object . assign = function ( ...objs ) { return { [ symbol ] : objs } ; } ;
165- return html ( statics , ...expr ) ;
166- }
167- finally {
168- Object . assign = assign ;
169- }
170- }
171152
172153 // The tagged template tag function name we're looking for.
173154 // This is static because it's generally assigned via htm.bind(h),
@@ -182,7 +163,7 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
182163 const statics = path . node . quasi . quasis . map ( e => e . value . raw ) ;
183164 const expr = path . node . quasi . expressions ;
184165
185- const tree = treeify ( statics , expr ) ;
166+ const tree = treeify ( build ( statics ) , expr ) ;
186167 const node = ! Array . isArray ( tree )
187168 ? transform ( tree , state )
188169 : t . arrayExpression ( tree . map ( root => transform ( root , state ) ) ) ;
@@ -191,4 +172,4 @@ export default function htmBabelPlugin({ types: t }, options = {}) {
191172 }
192173 }
193174 } ;
194- }
175+ }
0 commit comments