Blame view

node_modules/csso/lib/compressor/restructure/8-restructRuleset.js 5.82 KB
aaac7fed   liuqimichale   add
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
  var List = require('../../utils/list.js');
  var utils = require('./utils.js');
  var walkRulesRight = require('../../utils/walk.js').rulesRight;
  
  function calcSelectorLength(list) {
      var length = 0;
  
      list.each(function(data) {
          length += data.id.length + 1;
      });
  
      return length - 1;
  }
  
  function calcDeclarationsLength(tokens) {
      var length = 0;
  
      for (var i = 0; i < tokens.length; i++) {
          length += tokens[i].length;
      }
  
      return (
          length +          // declarations
          tokens.length - 1 // delimeters
      );
  }
  
  function processRuleset(node, item, list) {
      var avoidRulesMerge = this.stylesheet.avoidRulesMerge;
      var selectors = node.selector.selectors;
      var block = node.block;
      var disallowDownMarkers = Object.create(null);
      var allowMergeUp = true;
      var allowMergeDown = true;
  
      list.prevUntil(item.prev, function(prev, prevItem) {
          // skip non-ruleset node if safe
          if (prev.type !== 'Ruleset') {
              return utils.unsafeToSkipNode.call(selectors, prev);
          }
  
          var prevSelectors = prev.selector.selectors;
          var prevBlock = prev.block;
  
          if (node.pseudoSignature !== prev.pseudoSignature) {
              return true;
          }
  
          allowMergeDown = !prevSelectors.some(function(selector) {
              return selector.compareMarker in disallowDownMarkers;
          });
  
          // try prev ruleset if simpleselectors has no equal specifity and element selector
          if (!allowMergeDown && !allowMergeUp) {
              return true;
          }
  
          // try to join by selectors
          if (allowMergeUp && utils.isEqualLists(prevSelectors, selectors)) {
              prevBlock.declarations.appendList(block.declarations);
              list.remove(item);
              return true;
          }
  
          // try to join by properties
          var diff = utils.compareDeclarations(block.declarations, prevBlock.declarations);
  
          // console.log(diff.eq, diff.ne1, diff.ne2);
  
          if (diff.eq.length) {
              if (!diff.ne1.length && !diff.ne2.length) {
                  // equal blocks
                  if (allowMergeDown) {
                      utils.addSelectors(selectors, prevSelectors);
                      list.remove(prevItem);
                  }
  
                  return true;
              } else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
                                                TODO: need to be checked */
  
                  if (diff.ne1.length && !diff.ne2.length) {
                      // prevBlock is subset block
                      var selectorLength = calcSelectorLength(selectors);
                      var blockLength = calcDeclarationsLength(diff.eq); // declarations length
  
                      if (allowMergeUp && selectorLength < blockLength) {
                          utils.addSelectors(prevSelectors, selectors);
                          block.declarations = new List(diff.ne1);
                      }
                  } else if (!diff.ne1.length && diff.ne2.length) {
                      // node is subset of prevBlock
                      var selectorLength = calcSelectorLength(prevSelectors);
                      var blockLength = calcDeclarationsLength(diff.eq); // declarations length
  
                      if (allowMergeDown && selectorLength < blockLength) {
                          utils.addSelectors(selectors, prevSelectors);
                          prevBlock.declarations = new List(diff.ne2);
                      }
                  } else {
                      // diff.ne1.length && diff.ne2.length
                      // extract equal block
                      var newSelector = {
                          type: 'Selector',
                          info: {},
                          selectors: utils.addSelectors(prevSelectors.copy(), selectors)
                      };
                      var newBlockLength = calcSelectorLength(newSelector.selectors) + 2; // selectors length + curly braces length
                      var blockLength = calcDeclarationsLength(diff.eq); // declarations length
  
                      // create new ruleset if declarations length greater than
                      // ruleset description overhead
                      if (allowMergeDown && blockLength >= newBlockLength) {
                          var newRuleset = {
                              type: 'Ruleset',
                              info: {},
                              pseudoSignature: node.pseudoSignature,
                              selector: newSelector,
                              block: {
                                  type: 'Block',
                                  info: {},
                                  declarations: new List(diff.eq)
                              }
                          };
  
                          block.declarations = new List(diff.ne1);
                          prevBlock.declarations = new List(diff.ne2.concat(diff.ne2overrided));
                          list.insert(list.createItem(newRuleset), prevItem);
                          return true;
                      }
                  }
              }
          }
  
          if (allowMergeUp) {
              // TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
              // await property families to find property interception correctly
              allowMergeUp = !prevSelectors.some(function(prevSelector) {
                  return selectors.some(function(selector) {
                      return selector.compareMarker === prevSelector.compareMarker;
                  });
              });
          }
  
          prevSelectors.each(function(data) {
              disallowDownMarkers[data.compareMarker] = true;
          });
      });
  };
  
  module.exports = function restructRuleset(ast) {
      walkRulesRight(ast, function(node, item, list) {
          if (node.type === 'Ruleset') {
              processRuleset.call(this, node, item, list);
          }
      });
  };