@@ -27,7 +27,11 @@ const VOID_ELEMENTS = [
27
27
'wbr'
28
28
] ;
29
29
30
- const HOP = Object . prototype . hasOwnProperty ;
30
+ const objectKeys = Object . keys || ( obj => {
31
+ let keys = [ ] ;
32
+ for ( let i in obj ) if ( obj . hasOwnProperty ( i ) ) keys . push ( i ) ;
33
+ return keys ;
34
+ } ) ;
31
35
32
36
let encodeEntities = s => String ( s ) . replace ( / [ < > " & ] / g, escapeChar ) ;
33
37
@@ -109,9 +113,15 @@ export default function renderToString(vnode, context, opts, inner) {
109
113
let s = `<${ nodeName } ` ,
110
114
html ;
111
115
112
- for ( let name in attributes ) {
113
- if ( HOP . call ( attributes , name ) ) {
114
- let v = attributes [ name ] ;
116
+ if ( attributes ) {
117
+ let attrs = objectKeys ( attributes ) ;
118
+
119
+ // allow sorting lexicographically for more determinism (useful for tests, such as via preact-jsx-chai)
120
+ if ( opts && opts . sortAttributes === true ) attrs . sort ( ) ;
121
+
122
+ for ( let i = 0 ; i < attrs . length ; i ++ ) {
123
+ let name = attrs [ i ] ,
124
+ v = attributes [ name ] ;
115
125
if ( name === 'className' ) {
116
126
if ( attributes [ 'class' ] ) continue ;
117
127
name = 'class' ;
@@ -124,6 +134,7 @@ export default function renderToString(vnode, context, opts, inner) {
124
134
}
125
135
}
126
136
}
137
+
127
138
s += '>' ;
128
139
129
140
if ( html ) {
@@ -149,7 +160,7 @@ export default function renderToString(vnode, context, opts, inner) {
149
160
}
150
161
151
162
return s ;
152
- } ;
163
+ }
153
164
154
165
function getComponentName ( component ) {
155
166
return component . displayName || component . name || component . prototype . displayName || component . prototype . name || ( Function . prototype . toString . call ( component ) . match ( / \s ( [ ^ \( ] + ) / ) || EMPTY ) [ 1 ] || 'Component' ;
0 commit comments