1-mergeAtrule.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. var List = require('css-tree').List;
  2. var resolveKeyword = require('css-tree').keyword;
  3. var hasOwnProperty = Object.prototype.hasOwnProperty;
  4. var walk = require('css-tree').walk;
  5. function addRuleToMap(map, item, list, single) {
  6. var node = item.data;
  7. var name = resolveKeyword(node.name).basename;
  8. var id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);
  9. if (!hasOwnProperty.call(map, name)) {
  10. map[name] = Object.create(null);
  11. }
  12. if (single) {
  13. delete map[name][id];
  14. }
  15. if (!hasOwnProperty.call(map[name], id)) {
  16. map[name][id] = new List();
  17. }
  18. map[name][id].append(list.remove(item));
  19. }
  20. function relocateAtrules(ast, options) {
  21. var collected = Object.create(null);
  22. var topInjectPoint = null;
  23. ast.children.each(function(node, item, list) {
  24. if (node.type === 'Atrule') {
  25. var name = resolveKeyword(node.name).basename;
  26. switch (name) {
  27. case 'keyframes':
  28. addRuleToMap(collected, item, list, true);
  29. return;
  30. case 'media':
  31. if (options.forceMediaMerge) {
  32. addRuleToMap(collected, item, list, false);
  33. return;
  34. }
  35. break;
  36. }
  37. if (topInjectPoint === null &&
  38. name !== 'charset' &&
  39. name !== 'import') {
  40. topInjectPoint = item;
  41. }
  42. } else {
  43. if (topInjectPoint === null) {
  44. topInjectPoint = item;
  45. }
  46. }
  47. });
  48. for (var atrule in collected) {
  49. for (var id in collected[atrule]) {
  50. ast.children.insertList(
  51. collected[atrule][id],
  52. atrule === 'media' ? null : topInjectPoint
  53. );
  54. }
  55. }
  56. };
  57. function isMediaRule(node) {
  58. return node.type === 'Atrule' && node.name === 'media';
  59. }
  60. function processAtrule(node, item, list) {
  61. if (!isMediaRule(node)) {
  62. return;
  63. }
  64. var prev = item.prev && item.prev.data;
  65. if (!prev || !isMediaRule(prev)) {
  66. return;
  67. }
  68. // merge @media with same query
  69. if (node.prelude &&
  70. prev.prelude &&
  71. node.prelude.id === prev.prelude.id) {
  72. prev.block.children.appendList(node.block.children);
  73. list.remove(item);
  74. // TODO: use it when we can refer to several points in source
  75. // prev.loc = {
  76. // primary: prev.loc,
  77. // merged: node.loc
  78. // };
  79. }
  80. }
  81. module.exports = function rejoinAtrule(ast, options) {
  82. relocateAtrules(ast, options);
  83. walk(ast, {
  84. visit: 'Atrule',
  85. reverse: true,
  86. enter: processAtrule
  87. });
  88. };