7-mergeRuleset.js 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. var walk = require('css-tree').walk;
  2. var utils = require('./utils');
  3. /*
  4. At this step all rules has single simple selector. We try to join by equal
  5. declaration blocks to first rule, e.g.
  6. .a { color: red }
  7. b { ... }
  8. .b { color: red }
  9. ->
  10. .a, .b { color: red }
  11. b { ... }
  12. */
  13. function processRule(node, item, list) {
  14. var selectors = node.prelude.children;
  15. var declarations = node.block.children;
  16. var nodeCompareMarker = selectors.first().compareMarker;
  17. var skippedCompareMarkers = {};
  18. list.nextUntil(item.next, function(next, nextItem) {
  19. // skip non-ruleset node if safe
  20. if (next.type !== 'Rule') {
  21. return utils.unsafeToSkipNode.call(selectors, next);
  22. }
  23. if (node.pseudoSignature !== next.pseudoSignature) {
  24. return true;
  25. }
  26. var nextFirstSelector = next.prelude.children.head;
  27. var nextDeclarations = next.block.children;
  28. var nextCompareMarker = nextFirstSelector.data.compareMarker;
  29. // if next ruleset has same marked as one of skipped then stop joining
  30. if (nextCompareMarker in skippedCompareMarkers) {
  31. return true;
  32. }
  33. // try to join by selectors
  34. if (selectors.head === selectors.tail) {
  35. if (selectors.first().id === nextFirstSelector.data.id) {
  36. declarations.appendList(nextDeclarations);
  37. list.remove(nextItem);
  38. return;
  39. }
  40. }
  41. // try to join by properties
  42. if (utils.isEqualDeclarations(declarations, nextDeclarations)) {
  43. var nextStr = nextFirstSelector.data.id;
  44. selectors.some(function(data, item) {
  45. var curStr = data.id;
  46. if (nextStr < curStr) {
  47. selectors.insert(nextFirstSelector, item);
  48. return true;
  49. }
  50. if (!item.next) {
  51. selectors.insert(nextFirstSelector);
  52. return true;
  53. }
  54. });
  55. list.remove(nextItem);
  56. return;
  57. }
  58. // go to next ruleset if current one can be skipped (has no equal specificity nor element selector)
  59. if (nextCompareMarker === nodeCompareMarker) {
  60. return true;
  61. }
  62. skippedCompareMarkers[nextCompareMarker] = true;
  63. });
  64. }
  65. module.exports = function mergeRule(ast) {
  66. walk(ast, {
  67. visit: 'Rule',
  68. enter: processRule
  69. });
  70. };