| var List = require('css-tree').List; |
| var resolveKeyword = require('css-tree').keyword; |
| var hasOwnProperty = Object.prototype.hasOwnProperty; |
| var walk = require('css-tree').walk; |
| |
| function addRuleToMap(map, item, list, single) { |
| var node = item.data; |
| var name = resolveKeyword(node.name).basename; |
| var id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null); |
| |
| if (!hasOwnProperty.call(map, name)) { |
| map[name] = Object.create(null); |
| } |
| |
| if (single) { |
| delete map[name][id]; |
| } |
| |
| if (!hasOwnProperty.call(map[name], id)) { |
| map[name][id] = new List(); |
| } |
| |
| map[name][id].append(list.remove(item)); |
| } |
| |
| function relocateAtrules(ast, options) { |
| var collected = Object.create(null); |
| var topInjectPoint = null; |
| |
| ast.children.each(function(node, item, list) { |
| if (node.type === 'Atrule') { |
| var name = resolveKeyword(node.name).basename; |
| |
| switch (name) { |
| case 'keyframes': |
| addRuleToMap(collected, item, list, true); |
| return; |
| |
| case 'media': |
| if (options.forceMediaMerge) { |
| addRuleToMap(collected, item, list, false); |
| return; |
| } |
| break; |
| } |
| |
| if (topInjectPoint === null && |
| name !== 'charset' && |
| name !== 'import') { |
| topInjectPoint = item; |
| } |
| } else { |
| if (topInjectPoint === null) { |
| topInjectPoint = item; |
| } |
| } |
| }); |
| |
| for (var atrule in collected) { |
| for (var id in collected[atrule]) { |
| ast.children.insertList( |
| collected[atrule][id], |
| atrule === 'media' ? null : topInjectPoint |
| ); |
| } |
| } |
| }; |
| |
| function isMediaRule(node) { |
| return node.type === 'Atrule' && node.name === 'media'; |
| } |
| |
| function processAtrule(node, item, list) { |
| if (!isMediaRule(node)) { |
| return; |
| } |
| |
| var prev = item.prev && item.prev.data; |
| |
| if (!prev || !isMediaRule(prev)) { |
| return; |
| } |
| |
| // merge @media with same query |
| if (node.prelude && |
| prev.prelude && |
| node.prelude.id === prev.prelude.id) { |
| prev.block.children.appendList(node.block.children); |
| list.remove(item); |
| |
| // TODO: use it when we can refer to several points in source |
| // prev.loc = { |
| // primary: prev.loc, |
| // merged: node.loc |
| // }; |
| } |
| } |
| |
| module.exports = function rejoinAtrule(ast, options) { |
| relocateAtrules(ast, options); |
| |
| walk(ast, { |
| visit: 'Atrule', |
| reverse: true, |
| enter: processAtrule |
| }); |
| }; |