reduce-non-adjacent.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. var optimizeProperties = require('../properties/optimizer');
  2. var stringifyBody = require('../stringifier/one-time').body;
  3. var stringifySelectors = require('../stringifier/one-time').selectors;
  4. var isSpecial = require('./is-special');
  5. var cloneArray = require('../utils/clone-array');
  6. function reduceNonAdjacent(tokens, options, context) {
  7. var candidates = {};
  8. var repeated = [];
  9. for (var i = tokens.length - 1; i >= 0; i--) {
  10. var token = tokens[i];
  11. if (token[0] != 'selector')
  12. continue;
  13. if (token[2].length === 0)
  14. continue;
  15. var selectorAsString = stringifySelectors(token[1]);
  16. var isComplexAndNotSpecial = token[1].length > 1 && !isSpecial(options, selectorAsString);
  17. var wrappedSelectors = options.sourceMap ? wrappedSelectorsFrom(token[1]) : token[1];
  18. var selectors = isComplexAndNotSpecial ?
  19. [selectorAsString].concat(wrappedSelectors) :
  20. [selectorAsString];
  21. for (var j = 0, m = selectors.length; j < m; j++) {
  22. var selector = selectors[j];
  23. if (!candidates[selector])
  24. candidates[selector] = [];
  25. else
  26. repeated.push(selector);
  27. candidates[selector].push({
  28. where: i,
  29. list: wrappedSelectors,
  30. isPartial: isComplexAndNotSpecial && j > 0,
  31. isComplex: isComplexAndNotSpecial && j === 0
  32. });
  33. }
  34. }
  35. reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context);
  36. reduceComplexNonAdjacentCases(tokens, candidates, options, context);
  37. }
  38. function wrappedSelectorsFrom(list) {
  39. var wrapped = [];
  40. for (var i = 0; i < list.length; i++) {
  41. wrapped.push([list[i][0]]);
  42. }
  43. return wrapped;
  44. }
  45. function reduceSimpleNonAdjacentCases(tokens, repeated, candidates, options, context) {
  46. function filterOut(idx, bodies) {
  47. return data[idx].isPartial && bodies.length === 0;
  48. }
  49. function reduceBody(token, newBody, processedCount, tokenIdx) {
  50. if (!data[processedCount - tokenIdx - 1].isPartial)
  51. token[2] = newBody;
  52. }
  53. for (var i = 0, l = repeated.length; i < l; i++) {
  54. var selector = repeated[i];
  55. var data = candidates[selector];
  56. reduceSelector(tokens, selector, data, {
  57. filterOut: filterOut,
  58. callback: reduceBody
  59. }, options, context);
  60. }
  61. }
  62. function reduceComplexNonAdjacentCases(tokens, candidates, options, context) {
  63. var localContext = {};
  64. function filterOut(idx) {
  65. return localContext.data[idx].where < localContext.intoPosition;
  66. }
  67. function collectReducedBodies(token, newBody, processedCount, tokenIdx) {
  68. if (tokenIdx === 0)
  69. localContext.reducedBodies.push(newBody);
  70. }
  71. allSelectors:
  72. for (var complexSelector in candidates) {
  73. var into = candidates[complexSelector];
  74. if (!into[0].isComplex)
  75. continue;
  76. var intoPosition = into[into.length - 1].where;
  77. var intoToken = tokens[intoPosition];
  78. var reducedBodies = [];
  79. var selectors = isSpecial(options, complexSelector) ?
  80. [complexSelector] :
  81. into[0].list;
  82. localContext.intoPosition = intoPosition;
  83. localContext.reducedBodies = reducedBodies;
  84. for (var j = 0, m = selectors.length; j < m; j++) {
  85. var selector = selectors[j];
  86. var data = candidates[selector];
  87. if (data.length < 2)
  88. continue allSelectors;
  89. localContext.data = data;
  90. reduceSelector(tokens, selector, data, {
  91. filterOut: filterOut,
  92. callback: collectReducedBodies
  93. }, options, context);
  94. if (stringifyBody(reducedBodies[reducedBodies.length - 1]) != stringifyBody(reducedBodies[0]))
  95. continue allSelectors;
  96. }
  97. intoToken[2] = reducedBodies[0];
  98. }
  99. }
  100. function reduceSelector(tokens, selector, data, context, options, outerContext) {
  101. var bodies = [];
  102. var bodiesAsList = [];
  103. var joinsAt = [];
  104. var processedTokens = [];
  105. for (var j = data.length - 1, m = 0; j >= 0; j--) {
  106. if (context.filterOut(j, bodies))
  107. continue;
  108. var where = data[j].where;
  109. var token = tokens[where];
  110. var clonedBody = cloneArray(token[2]);
  111. bodies = bodies.concat(clonedBody);
  112. bodiesAsList.push(clonedBody);
  113. processedTokens.push(where);
  114. }
  115. for (j = 0, m = bodiesAsList.length; j < m; j++) {
  116. if (bodiesAsList[j].length > 0)
  117. joinsAt.push((joinsAt.length > 0 ? joinsAt[joinsAt.length - 1] : 0) + bodiesAsList[j].length);
  118. }
  119. optimizeProperties(selector, bodies, joinsAt, false, options, outerContext);
  120. var processedCount = processedTokens.length;
  121. var propertyIdx = bodies.length - 1;
  122. var tokenIdx = processedCount - 1;
  123. while (tokenIdx >= 0) {
  124. if ((tokenIdx === 0 || (bodies[propertyIdx] && bodiesAsList[tokenIdx].indexOf(bodies[propertyIdx]) > -1)) && propertyIdx > -1) {
  125. propertyIdx--;
  126. continue;
  127. }
  128. var newBody = bodies.splice(propertyIdx + 1);
  129. context.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
  130. tokenIdx--;
  131. }
  132. }
  133. module.exports = reduceNonAdjacent;