You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							195 lines
						
					
					
						
							5.1 KiB
						
					
					
				
			
		
		
	
	
							195 lines
						
					
					
						
							5.1 KiB
						
					
					
				var List = require('css-tree').List; | 
						|
var clone = require('css-tree').clone; | 
						|
var usageUtils = require('./usage'); | 
						|
var clean = require('./clean'); | 
						|
var replace = require('./replace'); | 
						|
var restructure = require('./restructure'); | 
						|
var walk = require('css-tree').walk; | 
						|
 | 
						|
function readChunk(children, specialComments) { | 
						|
    var buffer = new List(); | 
						|
    var nonSpaceTokenInBuffer = false; | 
						|
    var protectedComment; | 
						|
 | 
						|
    children.nextUntil(children.head, function(node, item, list) { | 
						|
        if (node.type === 'Comment') { | 
						|
            if (!specialComments || node.value.charAt(0) !== '!') { | 
						|
                list.remove(item); | 
						|
                return; | 
						|
            } | 
						|
 | 
						|
            if (nonSpaceTokenInBuffer || protectedComment) { | 
						|
                return true; | 
						|
            } | 
						|
 | 
						|
            list.remove(item); | 
						|
            protectedComment = node; | 
						|
            return; | 
						|
        } | 
						|
 | 
						|
        if (node.type !== 'WhiteSpace') { | 
						|
            nonSpaceTokenInBuffer = true; | 
						|
        } | 
						|
 | 
						|
        buffer.insert(list.remove(item)); | 
						|
    }); | 
						|
 | 
						|
    return { | 
						|
        comment: protectedComment, | 
						|
        stylesheet: { | 
						|
            type: 'StyleSheet', | 
						|
            loc: null, | 
						|
            children: buffer | 
						|
        } | 
						|
    }; | 
						|
} | 
						|
 | 
						|
function compressChunk(ast, firstAtrulesAllowed, num, options) { | 
						|
    options.logger('Compress block #' + num, null, true); | 
						|
 | 
						|
    var seed = 1; | 
						|
 | 
						|
    if (ast.type === 'StyleSheet') { | 
						|
        ast.firstAtrulesAllowed = firstAtrulesAllowed; | 
						|
        ast.id = seed++; | 
						|
    } | 
						|
 | 
						|
    walk(ast, { | 
						|
        visit: 'Atrule', | 
						|
        enter: function markScopes(node) { | 
						|
            if (node.block !== null) { | 
						|
                node.block.id = seed++; | 
						|
            } | 
						|
        } | 
						|
    }); | 
						|
    options.logger('init', ast); | 
						|
 | 
						|
    // remove redundant | 
						|
    clean(ast, options); | 
						|
    options.logger('clean', ast); | 
						|
 | 
						|
    // replace nodes for shortened forms | 
						|
    replace(ast, options); | 
						|
    options.logger('replace', ast); | 
						|
 | 
						|
    // structure optimisations | 
						|
    if (options.restructuring) { | 
						|
        restructure(ast, options); | 
						|
    } | 
						|
 | 
						|
    return ast; | 
						|
} | 
						|
 | 
						|
function getCommentsOption(options) { | 
						|
    var comments = 'comments' in options ? options.comments : 'exclamation'; | 
						|
 | 
						|
    if (typeof comments === 'boolean') { | 
						|
        comments = comments ? 'exclamation' : false; | 
						|
    } else if (comments !== 'exclamation' && comments !== 'first-exclamation') { | 
						|
        comments = false; | 
						|
    } | 
						|
 | 
						|
    return comments; | 
						|
} | 
						|
 | 
						|
function getRestructureOption(options) { | 
						|
    return 'restructure' in options ? options.restructure : | 
						|
           'restructuring' in options ? options.restructuring : | 
						|
           true; | 
						|
} | 
						|
 | 
						|
function wrapBlock(block) { | 
						|
    return new List().appendData({ | 
						|
        type: 'Rule', | 
						|
        loc: null, | 
						|
        prelude: { | 
						|
            type: 'SelectorList', | 
						|
            loc: null, | 
						|
            children: new List().appendData({ | 
						|
                type: 'Selector', | 
						|
                loc: null, | 
						|
                children: new List().appendData({ | 
						|
                    type: 'TypeSelector', | 
						|
                    loc: null, | 
						|
                    name: 'x' | 
						|
                }) | 
						|
            }) | 
						|
        }, | 
						|
        block: block | 
						|
    }); | 
						|
} | 
						|
 | 
						|
module.exports = function compress(ast, options) { | 
						|
    ast = ast || { type: 'StyleSheet', loc: null, children: new List() }; | 
						|
    options = options || {}; | 
						|
 | 
						|
    var compressOptions = { | 
						|
        logger: typeof options.logger === 'function' ? options.logger : function() {}, | 
						|
        restructuring: getRestructureOption(options), | 
						|
        forceMediaMerge: Boolean(options.forceMediaMerge), | 
						|
        usage: options.usage ? usageUtils.buildIndex(options.usage) : false | 
						|
    }; | 
						|
    var specialComments = getCommentsOption(options); | 
						|
    var firstAtrulesAllowed = true; | 
						|
    var input; | 
						|
    var output = new List(); | 
						|
    var chunk; | 
						|
    var chunkNum = 1; | 
						|
    var chunkChildren; | 
						|
 | 
						|
    if (options.clone) { | 
						|
        ast = clone(ast); | 
						|
    } | 
						|
 | 
						|
    if (ast.type === 'StyleSheet') { | 
						|
        input = ast.children; | 
						|
        ast.children = output; | 
						|
    } else { | 
						|
        input = wrapBlock(ast); | 
						|
    } | 
						|
 | 
						|
    do { | 
						|
        chunk = readChunk(input, Boolean(specialComments)); | 
						|
        compressChunk(chunk.stylesheet, firstAtrulesAllowed, chunkNum++, compressOptions); | 
						|
        chunkChildren = chunk.stylesheet.children; | 
						|
 | 
						|
        if (chunk.comment) { | 
						|
            // add \n before comment if there is another content in output | 
						|
            if (!output.isEmpty()) { | 
						|
                output.insert(List.createItem({ | 
						|
                    type: 'Raw', | 
						|
                    value: '\n' | 
						|
                })); | 
						|
            } | 
						|
 | 
						|
            output.insert(List.createItem(chunk.comment)); | 
						|
 | 
						|
            // add \n after comment if chunk is not empty | 
						|
            if (!chunkChildren.isEmpty()) { | 
						|
                output.insert(List.createItem({ | 
						|
                    type: 'Raw', | 
						|
                    value: '\n' | 
						|
                })); | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        if (firstAtrulesAllowed && !chunkChildren.isEmpty()) { | 
						|
            var lastRule = chunkChildren.last(); | 
						|
 | 
						|
            if (lastRule.type !== 'Atrule' || | 
						|
               (lastRule.name !== 'import' && lastRule.name !== 'charset')) { | 
						|
                firstAtrulesAllowed = false; | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        if (specialComments !== 'exclamation') { | 
						|
            specialComments = false; | 
						|
        } | 
						|
 | 
						|
        output.appendList(chunkChildren); | 
						|
    } while (!input.isEmpty()); | 
						|
 | 
						|
    return { | 
						|
        ast: ast | 
						|
    }; | 
						|
};
 | 
						|
 |