Skip to content

Commit 1bc10ba

Browse files
committed
Add sortAttributes option which, when set to true, sorts attributes by name lexicographically
1 parent c51aff1 commit 1bc10ba

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

src/index.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ const VOID_ELEMENTS = [
2727
'wbr'
2828
];
2929

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+
});
3135

3236
let encodeEntities = s => String(s).replace(/[<>"&]/g, escapeChar);
3337

@@ -109,9 +113,15 @@ export default function renderToString(vnode, context, opts, inner) {
109113
let s = `<${nodeName}`,
110114
html;
111115

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];
115125
if (name==='className') {
116126
if (attributes['class']) continue;
117127
name = 'class';
@@ -124,6 +134,7 @@ export default function renderToString(vnode, context, opts, inner) {
124134
}
125135
}
126136
}
137+
127138
s += '>';
128139

129140
if (html) {
@@ -149,7 +160,7 @@ export default function renderToString(vnode, context, opts, inner) {
149160
}
150161

151162
return s;
152-
};
163+
}
153164

154165
function getComponentName(component) {
155166
return component.displayName || component.name || component.prototype.displayName || component.prototype.name || (Function.prototype.toString.call(component).match(/\s([^\(]+)/) || EMPTY)[1] || 'Component';

test/render.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,16 @@ describe('render', () => {
242242
expect(rendered).to.equal('<div class="foo bar"></div>');
243243
});
244244
});
245+
246+
describe('sortAttributes', () => {
247+
it('should leave attributes unsorted by default', () => {
248+
let rendered = render(<div b1="b1" c="c" a="a" b="b" />);
249+
expect(rendered).to.equal('<div b1="b1" c="c" a="a" b="b"></div>');
250+
});
251+
252+
it('should sort attributes lexicographically if enabled', () => {
253+
let rendered = render(<div b1="b1" c="c" a="a" b="b" />, null, { sortAttributes:true });
254+
expect(rendered).to.equal('<div a="a" b="b" b1="b1" c="c"></div>');
255+
});
256+
});
245257
});

0 commit comments

Comments
 (0)