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 }); };