normalizeString.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
  4. var postcss = require('postcss');
  5. var valueParser = require('postcss-value-parser');
  6. /*
  7. * Constants (parser usage)
  8. */
  9. var SINGLE_QUOTE = 39;
  10. var DOUBLE_QUOTE = 34;
  11. var BACKSLASH = 92;
  12. var NEWLINE = 10;
  13. var SPACE = 32;
  14. var FEED = 12;
  15. var TAB = 9;
  16. var CR = 13;
  17. var WORD_END = /[ \n\t\r\f'"\\]/g;
  18. /*
  19. * Constants (node type strings)
  20. */
  21. var C_STRING = 'string';
  22. var C_ESCAPED_SINGLE_QUOTE = 'escapedSingleQuote';
  23. var C_ESCAPED_DOUBLE_QUOTE = 'escapedDoubleQuote';
  24. var C_SINGLE_QUOTE = 'singleQuote';
  25. var C_DOUBLE_QUOTE = 'doubleQuote';
  26. var C_NEWLINE = 'newline';
  27. var C_SINGLE = 'single';
  28. /*
  29. * Literals
  30. */
  31. var L_SINGLE_QUOTE = '\'';
  32. var L_DOUBLE_QUOTE = '"';
  33. var L_NEWLINE = '\\\n';
  34. /*
  35. * Parser nodes
  36. */
  37. var T_ESCAPED_SINGLE_QUOTE = { type: C_ESCAPED_SINGLE_QUOTE, value: '\\\'' };
  38. var T_ESCAPED_DOUBLE_QUOTE = { type: C_ESCAPED_DOUBLE_QUOTE, value: '\\"' };
  39. var T_SINGLE_QUOTE = { type: C_SINGLE_QUOTE, value: L_SINGLE_QUOTE };
  40. var T_DOUBLE_QUOTE = { type: C_DOUBLE_QUOTE, value: L_DOUBLE_QUOTE };
  41. var T_NEWLINE = { type: C_NEWLINE, value: L_NEWLINE };
  42. function stringify(ast) {
  43. return ast.nodes.reduce(function (str, _ref) {
  44. var value = _ref.value;
  45. // Collapse multiple line strings automatically
  46. if (value === L_NEWLINE) {
  47. return str;
  48. }
  49. return str + value;
  50. }, '');
  51. }
  52. function parse(str) {
  53. var code = void 0,
  54. next = void 0,
  55. value = void 0;
  56. var pos = 0;
  57. var len = str.length;
  58. var ast = {
  59. nodes: [],
  60. types: {
  61. escapedSingleQuote: 0,
  62. escapedDoubleQuote: 0,
  63. singleQuote: 0,
  64. doubleQuote: 0
  65. },
  66. quotes: false
  67. };
  68. while (pos < len) {
  69. code = str.charCodeAt(pos);
  70. switch (code) {
  71. case SPACE:
  72. case TAB:
  73. case CR:
  74. case FEED:
  75. next = pos;
  76. do {
  77. next += 1;
  78. code = str.charCodeAt(next);
  79. } while (code === SPACE || code === NEWLINE || code === TAB || code === CR || code === FEED);
  80. ast.nodes.push({
  81. type: 'space',
  82. value: str.slice(pos, next)
  83. });
  84. pos = next - 1;
  85. break;
  86. case SINGLE_QUOTE:
  87. ast.nodes.push(T_SINGLE_QUOTE);
  88. ast.types[C_SINGLE_QUOTE]++;
  89. ast.quotes = true;
  90. break;
  91. case DOUBLE_QUOTE:
  92. ast.nodes.push(T_DOUBLE_QUOTE);
  93. ast.types[C_DOUBLE_QUOTE]++;
  94. ast.quotes = true;
  95. break;
  96. case BACKSLASH:
  97. next = pos + 1;
  98. if (str.charCodeAt(next) === SINGLE_QUOTE) {
  99. ast.nodes.push(T_ESCAPED_SINGLE_QUOTE);
  100. ast.types[C_ESCAPED_SINGLE_QUOTE]++;
  101. ast.quotes = true;
  102. pos = next;
  103. break;
  104. } else if (str.charCodeAt(next) === DOUBLE_QUOTE) {
  105. ast.nodes.push(T_ESCAPED_DOUBLE_QUOTE);
  106. ast.types[C_ESCAPED_DOUBLE_QUOTE]++;
  107. ast.quotes = true;
  108. pos = next;
  109. break;
  110. } else if (str.charCodeAt(next) === NEWLINE) {
  111. ast.nodes.push(T_NEWLINE);
  112. pos = next;
  113. break;
  114. }
  115. /*
  116. * We need to fall through here to handle the token as
  117. * a whole word. The missing 'break' is intentional.
  118. */
  119. default:
  120. WORD_END.lastIndex = pos + 1;
  121. WORD_END.test(str);
  122. if (WORD_END.lastIndex === 0) {
  123. next = len - 1;
  124. } else {
  125. next = WORD_END.lastIndex - 2;
  126. }
  127. value = str.slice(pos, next + 1);
  128. ast.nodes.push({
  129. type: C_STRING,
  130. value: value
  131. });
  132. pos = next;
  133. }
  134. pos++;
  135. }
  136. return ast;
  137. }
  138. function changeWrappingQuotes(node, ast) {
  139. var types = ast.types;
  140. if (types[C_SINGLE_QUOTE] || types[C_DOUBLE_QUOTE]) {
  141. return;
  142. }
  143. if (node.quote === L_SINGLE_QUOTE && types[C_ESCAPED_SINGLE_QUOTE] > 0 && !types[C_ESCAPED_DOUBLE_QUOTE]) {
  144. node.quote = L_DOUBLE_QUOTE;
  145. }
  146. if (node.quote === L_DOUBLE_QUOTE && types[C_ESCAPED_DOUBLE_QUOTE] > 0 && !types[C_ESCAPED_SINGLE_QUOTE]) {
  147. node.quote = L_SINGLE_QUOTE;
  148. }
  149. ast.nodes = ast.nodes.reduce(function (newAst, child) {
  150. if (child.type === C_ESCAPED_DOUBLE_QUOTE && node.quote === L_SINGLE_QUOTE) {
  151. return [].concat(newAst, [T_DOUBLE_QUOTE]);
  152. }
  153. if (child.type === C_ESCAPED_SINGLE_QUOTE && node.quote === L_DOUBLE_QUOTE) {
  154. return [].concat(newAst, [T_SINGLE_QUOTE]);
  155. }
  156. return [].concat(newAst, [child]);
  157. }, []);
  158. }
  159. function normalize(value, preferredQuote) {
  160. if (!value || !value.length) {
  161. return value;
  162. }
  163. return valueParser(value).walk(function (child) {
  164. if (child.type !== C_STRING) {
  165. return;
  166. }
  167. var ast = parse(child.value);
  168. if (ast.quotes) {
  169. changeWrappingQuotes(child, ast);
  170. } else if (preferredQuote === C_SINGLE) {
  171. child.quote = L_SINGLE_QUOTE;
  172. } else {
  173. child.quote = L_DOUBLE_QUOTE;
  174. }
  175. child.value = stringify(ast);
  176. }).toString();
  177. }
  178. exports.default = postcss.plugin('cssnano-normalize-string', function (opts) {
  179. var _preferredQuote$opts = _extends({
  180. preferredQuote: 'double'
  181. }, opts),
  182. preferredQuote = _preferredQuote$opts.preferredQuote;
  183. return function (css) {
  184. css.walk(function (node) {
  185. if (node.type === 'rule') {
  186. node.selector = normalize(node.selector, preferredQuote);
  187. }
  188. if (node.type === 'decl') {
  189. node.value = normalize(node.value, preferredQuote);
  190. }
  191. if (node.type === 'atrule') {
  192. node.params = normalize(node.params, preferredQuote);
  193. }
  194. });
  195. };
  196. });
  197. module.exports = exports['default'];