extract-properties.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. var split = require('../utils/split');
  2. var COMMA = ',';
  3. var FORWARD_SLASH = '/';
  4. var AT_RULE = 'at-rule';
  5. var IMPORTANT_WORD = 'important';
  6. var IMPORTANT_TOKEN = '!'+IMPORTANT_WORD;
  7. var IMPORTANT_WORD_MATCH = new RegExp('^'+IMPORTANT_WORD+'$', 'i');
  8. var IMPORTANT_TOKEN_MATCH = new RegExp('^'+IMPORTANT_TOKEN+'$', 'i');
  9. function selectorName(value) {
  10. return value[0];
  11. }
  12. function noop() {}
  13. function withoutComments(string, into, heading, context) {
  14. var matcher = heading ? /^__ESCAPED_COMMENT_/ : /__ESCAPED_COMMENT_/;
  15. var track = heading ? context.track : noop; // don't track when comment not in a heading as we do it later in `trackComments`
  16. while (matcher.test(string)) {
  17. var startOfComment = string.indexOf('__');
  18. var endOfComment = string.indexOf('__', startOfComment + 1) + 2;
  19. var comment = string.substring(startOfComment, endOfComment);
  20. string = string.substring(0, startOfComment) + string.substring(endOfComment);
  21. track(comment);
  22. into.push(comment);
  23. }
  24. return string;
  25. }
  26. function withoutHeadingComments(string, into, context) {
  27. return withoutComments(string, into, true, context);
  28. }
  29. function withoutInnerComments(string, into, context) {
  30. return withoutComments(string, into, false, context);
  31. }
  32. function trackComments(comments, into, context) {
  33. for (var i = 0, l = comments.length; i < l; i++) {
  34. context.track(comments[i]);
  35. into.push(comments[i]);
  36. }
  37. }
  38. function extractProperties(string, selectors, context) {
  39. var list = [];
  40. var innerComments = [];
  41. var valueSeparator = /[\s,\/]/;
  42. if (typeof string != 'string')
  43. return [];
  44. if (string.indexOf(')') > -1)
  45. string = string.replace(/\)([^\s_;:,\)])/g, context.sourceMap ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ $1' : ') $1');
  46. if (string.indexOf('ESCAPED_URL_CLEAN_CSS') > -1)
  47. string = string.replace(/(ESCAPED_URL_CLEAN_CSS[^_]+?__)/g, context.sourceMap ? '$1 __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__ ' : '$1 ');
  48. var candidates = split(string, ';', false, '{', '}');
  49. for (var i = 0, l = candidates.length; i < l; i++) {
  50. var candidate = candidates[i];
  51. var firstColonAt = candidate.indexOf(':');
  52. var atRule = candidate.trim()[0] == '@';
  53. if (atRule) {
  54. context.track(candidate);
  55. list.push([AT_RULE, candidate.trim()]);
  56. continue;
  57. }
  58. if (firstColonAt == -1) {
  59. context.track(candidate);
  60. if (candidate.indexOf('__ESCAPED_COMMENT_SPECIAL') > -1)
  61. list.push(candidate.trim());
  62. continue;
  63. }
  64. if (candidate.indexOf('{') > 0 && candidate.indexOf('{') < firstColonAt) {
  65. context.track(candidate);
  66. continue;
  67. }
  68. var body = [];
  69. var name = candidate.substring(0, firstColonAt);
  70. innerComments = [];
  71. if (name.indexOf('__ESCAPED_COMMENT') > -1)
  72. name = withoutHeadingComments(name, list, context);
  73. if (name.indexOf('__ESCAPED_COMMENT') > -1)
  74. name = withoutInnerComments(name, innerComments, context);
  75. body.push([name.trim()].concat(context.track(name, true)));
  76. context.track(':');
  77. trackComments(innerComments, list, context);
  78. var firstBraceAt = candidate.indexOf('{');
  79. var isVariable = name.trim().indexOf('--') === 0;
  80. if (isVariable && firstBraceAt > 0) {
  81. var blockPrefix = candidate.substring(firstColonAt + 1, firstBraceAt + 1);
  82. var blockSuffix = candidate.substring(candidate.indexOf('}'));
  83. var blockContent = candidate.substring(firstBraceAt + 1, candidate.length - blockSuffix.length);
  84. context.track(blockPrefix);
  85. body.push(extractProperties(blockContent, selectors, context));
  86. list.push(body);
  87. context.track(blockSuffix);
  88. context.track(i < l - 1 ? ';' : '');
  89. continue;
  90. }
  91. var values = split(candidate.substring(firstColonAt + 1), valueSeparator, true);
  92. if (values.length == 1 && values[0] === '') {
  93. context.warnings.push('Empty property \'' + name + '\' inside \'' + selectors.filter(selectorName).join(',') + '\' selector. Ignoring.');
  94. continue;
  95. }
  96. for (var j = 0, m = values.length; j < m; j++) {
  97. var value = values[j];
  98. var trimmed = value.trim();
  99. if (trimmed.length === 0)
  100. continue;
  101. var lastCharacter = trimmed[trimmed.length - 1];
  102. var endsWithNonSpaceSeparator = trimmed.length > 1 && (lastCharacter == COMMA || lastCharacter == FORWARD_SLASH);
  103. if (endsWithNonSpaceSeparator)
  104. trimmed = trimmed.substring(0, trimmed.length - 1);
  105. if (trimmed.indexOf('__ESCAPED_COMMENT_CLEAN_CSS(0,-') > -1) {
  106. context.track(trimmed);
  107. continue;
  108. }
  109. innerComments = [];
  110. if (trimmed.indexOf('__ESCAPED_COMMENT') > -1)
  111. trimmed = withoutHeadingComments(trimmed, list, context);
  112. if (trimmed.indexOf('__ESCAPED_COMMENT') > -1)
  113. trimmed = withoutInnerComments(trimmed, innerComments, context);
  114. if (trimmed.length === 0) {
  115. trackComments(innerComments, list, context);
  116. continue;
  117. }
  118. var pos = body.length - 1;
  119. if (IMPORTANT_WORD_MATCH.test(trimmed) && body[pos][0] == '!') {
  120. context.track(trimmed);
  121. body[pos - 1][0] += IMPORTANT_TOKEN;
  122. body.pop();
  123. continue;
  124. }
  125. if (IMPORTANT_TOKEN_MATCH.test(trimmed) || (IMPORTANT_WORD_MATCH.test(trimmed) && body[pos][0][body[pos][0].length - 1] == '!')) {
  126. context.track(trimmed);
  127. body[pos][0] += trimmed;
  128. continue;
  129. }
  130. body.push([trimmed].concat(context.track(value, true)));
  131. trackComments(innerComments, list, context);
  132. if (endsWithNonSpaceSeparator) {
  133. body.push([lastCharacter]);
  134. context.track(lastCharacter);
  135. }
  136. }
  137. if (i < l - 1)
  138. context.track(';');
  139. list.push(body);
  140. }
  141. return list;
  142. }
  143. module.exports = extractProperties;