108 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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
 | |
|     });
 | |
| };
 |