Skip to content

Commit cb6a664

Browse files
committed
Preserve style order in prefixProperty
While attempting to update Aphrodite to inline-style-prefixer 3.0.0, I ran into an issue with prefixAll putting the prefixes in the wrong order. Specifically, they came after the un-prefixed style, which is not what we want because we want the standard style to have precedence in browsers that have it implemented. Khan/aphrodite#205 After a little bit of digging, I found that this was caused by the way prefixProperty was designed. To ensure that the prefixed styles are injected in the correct spot, we need to build a new object key-by-key and return it instead of mutating the style object that was passed in. This is likely to cause a performance hit if there are multiple styles to be prefixed in the same object. It might be worth making a pass to optimize this so all of the styles can be prefixed in one pass, but I'm going to leave that for another time. Although Object ordering is not guaranteed, it is generally sticky in most browsers so this seems like an improvement but not a total fix. This reliance on Object ordering will likely cause issues (different style order) when used on the server on older versions of Node (e.g. Node 4). There should probably be some effort similar to Khan/aphrodite#200 to ensure that the order is properly preserved throughout this package.
1 parent 34fcf25 commit cb6a664

File tree

4 files changed

+79
-7
lines changed

4 files changed

+79
-7
lines changed

modules/static/createPrefixer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ StaticData
4343
style[property] = processedValue
4444
}
4545

46-
prefixProperty(prefixMap, property, style)
46+
style = prefixProperty(prefixMap, property, style)
4747
}
4848
}
4949

modules/utils/prefixProperty.js

+22-6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,27 @@ export default function prefixProperty(
55
prefixProperties: Object,
66
property: string,
77
style: Object
8-
): void {
9-
if (prefixProperties.hasOwnProperty(property)) {
10-
const requiredPrefixes = prefixProperties[property]
11-
for (let i = 0, len = requiredPrefixes.length; i < len; ++i) {
12-
style[requiredPrefixes[i] + capitalizeString(property)] = style[property]
13-
}
8+
): Object {
9+
if (!prefixProperties.hasOwnProperty(property)) {
10+
return style
1411
}
12+
13+
// We need to preserve the order of the styles while inserting new prefixed
14+
// styles. Object order is not guaranteed, but this is better than nothing.
15+
// Note that this is brittle and is likely to break in older versions of
16+
// Node (e.g. Node 4).
17+
const newStyle = {}
18+
Object.keys(style).forEach((styleProperty) => {
19+
if (styleProperty === property) {
20+
// We've found the style we need to prefix.
21+
const requiredPrefixes = prefixProperties[property]
22+
for (let i = 0, len = requiredPrefixes.length; i < len; ++i) {
23+
newStyle[requiredPrefixes[i] + capitalizeString(property)] = style[property]
24+
}
25+
}
26+
27+
newStyle[styleProperty] = style[styleProperty]
28+
})
29+
30+
return newStyle
1531
}

test/dynamic/createPrefixer-test.js

+28
Original file line numberDiff line numberDiff line change
@@ -386,5 +386,33 @@ describe('Dynamic Prefixer', () => {
386386
}
387387
expect(Prefixer.prefixAll(input)).to.eql(output)
388388
})
389+
390+
it('puts the prefixes in the correct order', () => {
391+
const input = { userSelect: 'none' }
392+
const order = [
393+
'WebkitUserSelect',
394+
'MozUserSelect',
395+
'msUserSelect',
396+
'userSelect'
397+
]
398+
expect(Object.keys(Prefixer.prefixAll(input))).to.eql(order)
399+
});
400+
401+
it('does not mess up the order of other styles', () => {
402+
const input = {
403+
color: 'red',
404+
userSelect: 'none',
405+
border: 0
406+
}
407+
const order = [
408+
'color',
409+
'WebkitUserSelect',
410+
'MozUserSelect',
411+
'msUserSelect',
412+
'userSelect',
413+
'border'
414+
]
415+
expect(Object.keys(Prefixer.prefixAll(input))).to.eql(order)
416+
});
389417
})
390418
})

test/static/createPrefixer-test.js

+28
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ describe('Static Prefixer', () => {
2121
expect(prefixAll(input)).to.eql(output)
2222
})
2323

24+
it('puts the prefixes in the correct order', () => {
25+
const input = { userSelect: 'none' }
26+
const order = [
27+
'WebkitUserSelect',
28+
'MozUserSelect',
29+
'msUserSelect',
30+
'userSelect'
31+
]
32+
expect(Object.keys(prefixAll(input))).to.eql(order)
33+
});
34+
35+
it('does not mess up the order of other styles', () => {
36+
const input = {
37+
color: 'red',
38+
userSelect: 'none',
39+
border: 0
40+
}
41+
const order = [
42+
'color',
43+
'WebkitUserSelect',
44+
'MozUserSelect',
45+
'msUserSelect',
46+
'userSelect',
47+
'border'
48+
]
49+
expect(Object.keys(prefixAll(input))).to.eql(order)
50+
});
51+
2452
it('should use dash-cased alternative values in array', () => {
2553
const input = { marginLeft: 'calc(30deg)' }
2654
const output = { marginLeft: ['-webkit-calc(30deg)', '-moz-calc(30deg)', 'calc(30deg)'] }

0 commit comments

Comments
 (0)