optimization-level.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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. transform: noop
  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 noop() {}
  59. function optimizationLevelFrom(source) {
  60. var level = override(DEFAULTS, {});
  61. var Zero = OptimizationLevel.Zero;
  62. var One = OptimizationLevel.One;
  63. var Two = OptimizationLevel.Two;
  64. if (undefined === source) {
  65. delete level[Two];
  66. return level;
  67. }
  68. if (typeof source == 'string') {
  69. source = parseInt(source);
  70. }
  71. if (typeof source == 'number' && source === parseInt(Two)) {
  72. return level;
  73. }
  74. if (typeof source == 'number' && source === parseInt(One)) {
  75. delete level[Two];
  76. return level;
  77. }
  78. if (typeof source == 'number' && source === parseInt(Zero)) {
  79. delete level[Two];
  80. delete level[One];
  81. return level;
  82. }
  83. if (typeof source == 'object') {
  84. source = covertValuesToHashes(source);
  85. }
  86. if (One in source && 'roundingPrecision' in source[One]) {
  87. source[One].roundingPrecision = roundingPrecisionFrom(source[One].roundingPrecision);
  88. }
  89. if (Two in source && 'skipProperties' in source[Two] && typeof(source[Two].skipProperties) == 'string') {
  90. source[Two].skipProperties = source[Two].skipProperties.split(LIST_VALUE_SEPARATOR);
  91. }
  92. if (Zero in source || One in source || Two in source) {
  93. level[Zero] = override(level[Zero], source[Zero]);
  94. }
  95. if (One in source && ALL_KEYWORD_1 in source[One]) {
  96. level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_1])));
  97. delete source[One][ALL_KEYWORD_1];
  98. }
  99. if (One in source && ALL_KEYWORD_2 in source[One]) {
  100. level[One] = override(level[One], defaults(One, normalizeValue(source[One][ALL_KEYWORD_2])));
  101. delete source[One][ALL_KEYWORD_2];
  102. }
  103. if (One in source || Two in source) {
  104. level[One] = override(level[One], source[One]);
  105. } else {
  106. delete level[One];
  107. }
  108. if (Two in source && ALL_KEYWORD_1 in source[Two]) {
  109. level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_1])));
  110. delete source[Two][ALL_KEYWORD_1];
  111. }
  112. if (Two in source && ALL_KEYWORD_2 in source[Two]) {
  113. level[Two] = override(level[Two], defaults(Two, normalizeValue(source[Two][ALL_KEYWORD_2])));
  114. delete source[Two][ALL_KEYWORD_2];
  115. }
  116. if (Two in source) {
  117. level[Two] = override(level[Two], source[Two]);
  118. } else {
  119. delete level[Two];
  120. }
  121. return level;
  122. }
  123. function defaults(level, value) {
  124. var options = override(DEFAULTS[level], {});
  125. var key;
  126. for (key in options) {
  127. if (typeof options[key] == 'boolean') {
  128. options[key] = value;
  129. }
  130. }
  131. return options;
  132. }
  133. function normalizeValue(value) {
  134. switch (value) {
  135. case FALSE_KEYWORD_1:
  136. case FALSE_KEYWORD_2:
  137. return false;
  138. case TRUE_KEYWORD_1:
  139. case TRUE_KEYWORD_2:
  140. return true;
  141. default:
  142. return value;
  143. }
  144. }
  145. function covertValuesToHashes(source) {
  146. var clonedSource = override(source, {});
  147. var level;
  148. var i;
  149. for (i = 0; i <= 2; i++) {
  150. level = '' + i;
  151. if (level in clonedSource && (clonedSource[level] === undefined || clonedSource[level] === false)) {
  152. delete clonedSource[level];
  153. }
  154. if (level in clonedSource && clonedSource[level] === true) {
  155. clonedSource[level] = {};
  156. }
  157. if (level in clonedSource && typeof clonedSource[level] == 'string') {
  158. clonedSource[level] = covertToHash(clonedSource[level], level);
  159. }
  160. }
  161. return clonedSource;
  162. }
  163. function covertToHash(asString, level) {
  164. return asString
  165. .split(OPTION_SEPARATOR)
  166. .reduce(function (accumulator, directive) {
  167. var parts = directive.split(OPTION_VALUE_SEPARATOR);
  168. var name = parts[0];
  169. var value = parts[1];
  170. var normalizedValue = normalizeValue(value);
  171. if (ALL_KEYWORD_1 == name || ALL_KEYWORD_2 == name) {
  172. accumulator = override(accumulator, defaults(level, normalizedValue));
  173. } else {
  174. accumulator[name] = normalizedValue;
  175. }
  176. return accumulator;
  177. }, {});
  178. }
  179. module.exports = {
  180. OptimizationLevel: OptimizationLevel,
  181. optimizationLevelFrom: optimizationLevelFrom,
  182. };