optimization-level.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. var roundingPrecisionFrom = require('./rounding-precision').roundingPrecisionFrom;
  2. var override = require('../utils/override');
  3. var OptimizationLevel = {
  4. Zero: '0',
  5. One: '1',
  6. Two: '2'
  7. };
  8. var DEFAULTS = {};
  9. DEFAULTS[OptimizationLevel.Zero] = {};
  10. DEFAULTS[OptimizationLevel.One] = {
  11. cleanupCharsets: true,
  12. normalizeUrls: true,
  13. optimizeBackground: true,
  14. optimizeBorderRadius: true,
  15. optimizeFilter: true,
  16. optimizeFontWeight: true,
  17. optimizeOutline: true,
  18. removeEmpty: true,
  19. removeNegativePaddings: true,
  20. removeQuotes: true,
  21. removeWhitespace: true,
  22. replaceMultipleZeros: true,
  23. replaceTimeUnits: true,
  24. replaceZeroUnits: true,
  25. roundingPrecision: roundingPrecisionFrom(undefined),
  26. selectorsSortingMethod: 'standard',
  27. specialComments: 'all',
  28. tidyAtRules: true,
  29. tidyBlockScopes: true,
  30. tidySelectors: true,
  31. variableValueOptimizers: []
  32. };
  33. DEFAULTS[OptimizationLevel.Two] = {
  34. mergeAdjacentRules: true,
  35. mergeIntoShorthands: true,
  36. mergeMedia: true,
  37. mergeNonAdjacentRules: true,
  38. mergeSemantically: false,
  39. overrideProperties: true,
  40. removeEmpty: true,
  41. reduceNonAdjacentRules: true,
  42. removeDuplicateFontRules: true,
  43. removeDuplicateMediaBlocks: true,
  44. removeDuplicateRules: true,
  45. removeUnusedAtRules: false,
  46. restructureRules: false,
  47. skipProperties: []
  48. };
  49. var ALL_KEYWORD_1 = '*';
  50. var ALL_KEYWORD_2 = 'all';
  51. var FALSE_KEYWORD_1 = 'false';
  52. var FALSE_KEYWORD_2 = 'off';
  53. var TRUE_KEYWORD_1 = 'true';
  54. var TRUE_KEYWORD_2 = 'on';
  55. var LIST_VALUE_SEPARATOR = ',';
  56. var OPTION_SEPARATOR = ';';
  57. var OPTION_VALUE_SEPARATOR = ':';
  58. function optimizationLevelFrom(source) {
  59. var level = override(DEFAULTS, {});
  60. var Zero = OptimizationLevel.Zero;
  61. var One = OptimizationLevel.One;
  62. var Two = OptimizationLevel.Two;
  63. if (undefined === source) {
  64. delete level[Two];
  65. return level;
  66. }
  67. if (typeof source == 'string') {
  68. source = parseInt(source);
  69. }
  70. if (typeof source == 'number' && source === parseInt(Two)) {
  71. return level;
  72. }
  73. if (typeof source == 'number' && source === parseInt(One)) {
  74. delete level[Two];
  75. return level;
  76. }
  77. if (typeof source == 'number' && source === parseInt(Zero)) {
  78. delete level[Two];
  79. delete level[One];
  80. return level;
  81. }
  82. if (typeof source == 'object') {
  83. source = covertValuesToHashes(source);
  84. }
  85. if (One in source && 'roundingPrecision' in source[One]) {
  86. source[One].roundingPrecision = roundingPrecisionFrom(source[One].roundingPrecision);
  87. }
  88. if (Two in source && 'skipProperties' in source[Two] && typeof (source[Two].skipProperties) == 'string') {
  89. source[Two].skipProperties = source[Two].skipProperties.split(LIST_VALUE_SEPARATOR);
  90. }
  91. if (Zero in source || One in source || Two in source) {
  92. level[Zero] = override(level[Zero], source[Zero]);
  93. }
  94. if (One in source && ALL_KEYWORD_1 in source[One]) {
  95. level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_1])));
  96. delete source[One][ALL_KEYWORD_1];
  97. }
  98. if (One in source && ALL_KEYWORD_2 in source[One]) {
  99. level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_2])));
  100. delete source[One][ALL_KEYWORD_2];
  101. }
  102. if (One in source || Two in source) {
  103. level[One] = override(level[One], source[One]);
  104. } else {
  105. delete level[One];
  106. }
  107. if (Two in source && ALL_KEYWORD_1 in source[Two]) {
  108. level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_1])));
  109. delete source[Two][ALL_KEYWORD_1];
  110. }
  111. if (Two in source && ALL_KEYWORD_2 in source[Two]) {
  112. level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_2])));
  113. delete source[Two][ALL_KEYWORD_2];
  114. }
  115. if (Two in source) {
  116. level[Two] = override(level[Two], source[Two]);
  117. } else {
  118. delete level[Two];
  119. }
  120. return level;
  121. }
  122. function defaults(level, value) {
  123. var options = override(DEFAULTS[level], {});
  124. var key;
  125. for (key in options) {
  126. if (typeof options[key] == 'boolean') {
  127. options[key] = value;
  128. }
  129. }
  130. return options;
  131. }
  132. function normalizeValue(value) {
  133. switch (value) {
  134. case FALSE_KEYWORD_1:
  135. case FALSE_KEYWORD_2:
  136. return false;
  137. case TRUE_KEYWORD_1:
  138. case TRUE_KEYWORD_2:
  139. return true;
  140. default:
  141. return value;
  142. }
  143. }
  144. function covertValuesToHashes(source) {
  145. var clonedSource = override(source, {});
  146. var level;
  147. var i;
  148. for (i = 0; i <= 2; i++) {
  149. level = '' + i;
  150. if (level in clonedSource && (clonedSource[level] === undefined || clonedSource[level] === false)) {
  151. delete clonedSource[level];
  152. }
  153. if (level in clonedSource && clonedSource[level] === true) {
  154. clonedSource[level] = {};
  155. }
  156. if (level in clonedSource && typeof clonedSource[level] == 'string') {
  157. clonedSource[level] = covertToHash(clonedSource[level], level);
  158. }
  159. }
  160. return clonedSource;
  161. }
  162. function covertToHash(asString, level) {
  163. return asString
  164. .split(OPTION_SEPARATOR)
  165. .reduce(function(accumulator, directive) {
  166. var parts = directive.split(OPTION_VALUE_SEPARATOR);
  167. var name = parts[0];
  168. var value = parts[1];
  169. var normalizedValue = normalizeValue(value);
  170. if (ALL_KEYWORD_1 == name || ALL_KEYWORD_2 == name) {
  171. accumulator = override(accumulator, defaults(level, normalizedValue));
  172. } else {
  173. accumulator[name] = normalizedValue;
  174. }
  175. return accumulator;
  176. }, {});
  177. }
  178. module.exports = {
  179. OptimizationLevel: OptimizationLevel,
  180. optimizationLevelFrom: optimizationLevelFrom
  181. };