given the following prop file :
{
"color": {
"grey": {
"000": {
"value": "#FFFFFF"
},
"100": {
"value": "#EEEEF0"
},
"200": {
"value": "#D3D2D6"
},
},
}
}
will output in this order :
color-grey-100color-grey-200color-grey-000this is not a problem when using flattened variables/tokens, but is not optimal for JSON, scss maps or javascript objects where order is important (when iterating through).
I knew this would come up eventually... Going from a tree structure to a flat array can sometimes produce weird behaviors. I don't know if there is a way to enforce the order at the property file level. Here is the function that flattens all the property tree structure:
https://github.com/amzn/style-dictionary/blob/master/lib/utils/flattenProperties.js
If you needed a specific order in an output file, you could write a custom format and in the format you can sort the properties. A quick and dirty example:
const StyleDictionary = require('style-dictionary');
StyleDictionary.registerFormat({
name: 'mySortedFormat',
formatter: (dictionary, config) => {
// .concat() so we don't sort the array in place
const sortedProperties = dictionary.allProperties.concat().sort((a,b) => {
// sorting function
});
return sortedProperties.map((prop) => {
// map propers to string
})
.join('\n');
}
});
//...
Javascript is terrible about the order of keys in an object.
The behavior is explicitly left undefined by the ECMAScript specification. In ECMA-262, section 12.6.4 (for-in):
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified.
and later, in 15.2.3.14 (Object.keys):
If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used in step 5 of this algorithm.
and worse, in 15.12.2 (JSON.parse), when describing how to parse JSON into an Object:
The ordering of the Strings should be the same as that used by the Object.keys standard built-in function.
Thus, consistency is enforced per-implementation, but not across implementations. Most browsers respect definition order. Chrome and Opera do as well, but only for non-numerical property names. In these two browsers, the properties are pulled by numerical property keys frist, then in definition order (this is has to do with how they implement arrays). This was listed as a bug in chrome at one point, but they said it was working as intended.
Style Dictionary is built on top of Node, and Node is built from V8 (the Chrome JS engine), so it has the same ordering implementation (and problems for you).
It is possible that we could define our own sort order. However, there are 3 big problems in doing this:
The specific behavior in your example is due to one 'gotcha': any key consisting of a string of numbers with a leading 0 is treated as a string and not as a numerical key. Any string of numbers without a leading 0 is treated as a numerical. Thus the string "000" is treated as non-numerical and the strings "100" and "200" are treated as numerical. "0" is numerical, but "00" is not.
You can ensure consistent behavior by forcing the keys to be treated as all strings or all numericals. This will give you the behavior you are looking for.
All numericals:
All strings:
You can play with how your browser of choice implements key order behavior with for-in here (use Chrome to see the behavior you will get with Style Dictionary):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
I hope this helps explain why you see this behavior, and why defining a more "correct" behavior would be close to impossible.
Closing, feel free to re-open if you have additional questions.
Most helpful comment
Javascript is terrible about the order of keys in an object.
The behavior is explicitly left undefined by the ECMAScript specification. In ECMA-262, section 12.6.4 (for-in):
and later, in 15.2.3.14 (Object.keys):
and worse, in 15.12.2 (JSON.parse), when describing how to parse JSON into an Object:
Thus, consistency is enforced per-implementation, but not across implementations. Most browsers respect definition order. Chrome and Opera do as well, but only for non-numerical property names. In these two browsers, the properties are pulled by numerical property keys frist, then in definition order (this is has to do with how they implement arrays). This was listed as a bug in chrome at one point, but they said it was working as intended.
Style Dictionary is built on top of Node, and Node is built from V8 (the Chrome JS engine), so it has the same ordering implementation (and problems for you).
It is possible that we could define our own sort order. However, there are 3 big problems in doing this:
The specific behavior in your example is due to one 'gotcha': any key consisting of a string of numbers with a leading 0 is treated as a string and not as a numerical key. Any string of numbers without a leading 0 is treated as a numerical. Thus the string "000" is treated as non-numerical and the strings "100" and "200" are treated as numerical. "0" is numerical, but "00" is not.
You can ensure consistent behavior by forcing the keys to be treated as all strings or all numericals. This will give you the behavior you are looking for.
All numericals:
All strings:
You can play with how your browser of choice implements key order behavior with for-in here (use Chrome to see the behavior you will get with Style Dictionary):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
I hope this helps explain why you see this behavior, and why defining a more "correct" behavior would be close to impossible.