diff --git a/README.md b/README.md index e31711c..eebf01c 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,13 @@ postcss([ ``` +### `preserveAtRulesOrder` (default: `false`) + +Keeps your at-rules like media queries in the order to defined them. + +Ideally, this would be defaulted to `true` and it will be in the next major version. All of the tests expecations need to be updated and probably just drop support for `preserveAtRulesOrder: false` + + # Quick Reference/Notes - This plugin was spawned out of a [discussion on the `cssnext` repo](https://github.com/cssnext/cssnext/issues/99). diff --git a/index.js b/index.js index 2c8d683..abcfe8d 100644 --- a/index.js +++ b/index.js @@ -65,7 +65,10 @@ var defaults = { variables: {}, // Preserve variables injected via JS with the `variables` option above // before serializing to CSS (`false` will remove these variables from output) - preserveInjectedVariables: true + preserveInjectedVariables: true, + // Will write media queries in the same order as in the original file. + // Currently defaulted to false for legacy behavior. We can update to `true` in a major version + preserveAtRulesOrder: false }; module.exports = postcss.plugin('postcss-css-variables', function(options) { @@ -249,7 +252,7 @@ module.exports = postcss.plugin('postcss-css-variables', function(options) { ruleToWorkOn.nodes.slice(0).forEach(function(node) { if(node.type === 'decl') { var decl = node; - resolveDecl(decl, map, opts.preserve, logResolveValueResult); + resolveDecl(decl, map, opts.preserve, opts.preserveAtRulesOrder, logResolveValueResult); } }); }); diff --git a/lib/resolve-decl.js b/lib/resolve-decl.js index cae8e03..214f570 100644 --- a/lib/resolve-decl.js +++ b/lib/resolve-decl.js @@ -71,8 +71,9 @@ function eachMapItemDependencyOfDecl(variablesUsedList, map, decl, cb) { // Resolve the decl with the computed value // Also add in any media queries that change the value as necessary -function resolveDecl(decl, map, /*optional*/shouldPreserve, /*optional*/logResolveValueResult) { +function resolveDecl(decl, map, /*optional*/shouldPreserve, /*optional*/preserveAtRulesOrder, /*optional*/logResolveValueResult) { shouldPreserve = shouldPreserve || false; + preserveAtRulesOrder = preserveAtRulesOrder || false; // Make it chainable var _logResolveValueResult = function(valueResults) { @@ -93,6 +94,7 @@ function resolveDecl(decl, map, /*optional*/shouldPreserve, /*optional*/logResol // Resolve the cascade dependencies // Now find any at-rule declarations that need to be added below each rule //console.log('resolveDecl 2'); + var previousAtRuleNode; eachMapItemDependencyOfDecl(valueResults.variablesUsed, map, decl, function(mimicDecl, mapItem) { var ruleClone = shallowCloneNode(decl.parent); var declClone = decl.clone(); @@ -129,14 +131,19 @@ function resolveDecl(decl, map, /*optional*/shouldPreserve, /*optional*/logResol currentAtRuleNode = currentAtRuleNode.parent; } - // Put the atRuleStructure after the declaration's rule - decl.parent.parent.insertAfter(decl.parent, parentAtRuleNode); + // Put the first atRuleStructure after the declaration's rule, + // and after that, put them right after the previous one + decl.parent.parent.insertAfter(preserveAtRulesOrder && previousAtRuleNode || decl.parent, parentAtRuleNode); + + // Save referance of previous atRuleStructure + previousAtRuleNode = parentAtRuleNode } else { ruleClone.selector = mimicDecl.parent.selector; - // Put the atRuleStructure after the declaration's rule - decl.parent.parent.insertAfter(decl.parent, ruleClone); + // Put the first atRuleStructure after the declaration's rule, + // and after that, put them right after the previous one + decl.parent.parent.insertAfter(preserveAtRulesOrder && previousAtRuleNode || decl.parent, ruleClone); } }); diff --git a/test/fixtures/media-query-nested-preserver-rule-order.css b/test/fixtures/media-query-nested-preserver-rule-order.css new file mode 100644 index 0000000..673fa9e --- /dev/null +++ b/test/fixtures/media-query-nested-preserver-rule-order.css @@ -0,0 +1,21 @@ +:root { + --width: 150px; +} + +@media print { + @media (min-width: 1000px) { + :root { + --width: 300px; + } + + @media (min-width: 1250px) { + :root { + --width: 450px; + } + } + } +} + +.box { + width: var(--width); +} diff --git a/test/fixtures/media-query-nested-preserver-rule-order.expected.css b/test/fixtures/media-query-nested-preserver-rule-order.expected.css new file mode 100644 index 0000000..8cee7c3 --- /dev/null +++ b/test/fixtures/media-query-nested-preserver-rule-order.expected.css @@ -0,0 +1,26 @@ +.box { + width: 150px; +} + +@media print { + + @media (min-width: 1000px) { + + .box{ + width: 300px; + } + } +} + +@media print { + + @media (min-width: 1000px) { + + @media (min-width: 1250px) { + + .box { + width: 450px; + } + } + } +} diff --git a/test/fixtures/media-query-preserve-rule-order.css b/test/fixtures/media-query-preserve-rule-order.css new file mode 100644 index 0000000..586b1f1 --- /dev/null +++ b/test/fixtures/media-query-preserve-rule-order.css @@ -0,0 +1,20 @@ +:root { + --width: 150px; +} + +@media (min-width: 1000px) { + :root { + --width: 300px; + } +} + +@media (min-width: 1250px) { + :root { + --width: 450px; + } +} + +.box { + width: var(--width); + +} diff --git a/test/fixtures/media-query-preserve-rule-order.expected.css b/test/fixtures/media-query-preserve-rule-order.expected.css new file mode 100644 index 0000000..3382a99 --- /dev/null +++ b/test/fixtures/media-query-preserve-rule-order.expected.css @@ -0,0 +1,15 @@ +.box { + width: 150px; +} + +@media (min-width: 1000px) { + .box { + width: 300px; + } +} + +@media (min-width: 1250px) { + .box { + width: 450px; + } +} diff --git a/test/test.js b/test/test.js index f0879f7..9de4142 100644 --- a/test/test.js +++ b/test/test.js @@ -135,7 +135,11 @@ describe('postcss-css-variables', function() { test('should add rule declaration of property in @media', 'media-query'); test('should add rule declaration of property in @support', 'support-directive'); - test('should work with nested @media', 'media-query-nested'); + test('should work with @media, preserving rule order', 'media-query-preserve-rule-order', { preserveAtRulesOrder: true }); + + test('should work with nested @media', 'media-query-nested', { preserveAtRulesOrder: false }); + test('should work with nested @media, preserving rule order', 'media-query-nested-preserver-rule-order', { preserveAtRulesOrder: true }); + test('should cascade to nested rules', 'cascade-on-nested-rules');