123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- var List = require('../utils/list');
- var clone = require('../utils/clone');
- var usageUtils = require('./usage');
- var clean = require('./clean');
- var compress = require('./compress');
- var restructureBlock = require('./restructure');
- var walkRules = require('../utils/walk').rules;
- function readRulesChunk(rules, specialComments) {
- var buffer = new List();
- var nonSpaceTokenInBuffer = false;
- var protectedComment;
- rules.nextUntil(rules.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 !== 'Space') {
- nonSpaceTokenInBuffer = true;
- }
- buffer.insert(list.remove(item));
- });
- return {
- comment: protectedComment,
- stylesheet: {
- type: 'StyleSheet',
- info: null,
- rules: buffer
- }
- };
- }
- function compressChunk(ast, firstAtrulesAllowed, usageData, num, logger) {
- logger('Compress block #' + num, null, true);
- var seed = 1;
- walkRules(ast, function markStylesheets() {
- if ('id' in this.stylesheet === false) {
- this.stylesheet.firstAtrulesAllowed = firstAtrulesAllowed;
- this.stylesheet.id = seed++;
- }
- });
- logger('init', ast);
- // remove redundant
- clean(ast, usageData);
- logger('clean', ast);
- // compress nodes
- compress(ast, usageData);
- logger('compress', ast);
- 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([{
- type: 'Ruleset',
- selector: {
- type: 'Selector',
- selectors: new List([{
- type: 'SimpleSelector',
- sequence: new List([{
- type: 'Identifier',
- name: 'x'
- }])
- }])
- },
- block: block
- }]);
- }
- module.exports = function compress(ast, options) {
- ast = ast || { type: 'StyleSheet', info: null, rules: new List() };
- options = options || {};
- var logger = typeof options.logger === 'function' ? options.logger : Function();
- var specialComments = getCommentsOption(options);
- var restructuring = getRestructureOption(options);
- var firstAtrulesAllowed = true;
- var usageData = false;
- var inputRules;
- var outputRules = new List();
- var chunk;
- var chunkNum = 1;
- var chunkRules;
- if (options.clone) {
- ast = clone(ast);
- }
- if (ast.type === 'StyleSheet') {
- inputRules = ast.rules;
- ast.rules = outputRules;
- } else {
- inputRules = wrapBlock(ast);
- }
- if (options.usage) {
- usageData = usageUtils.buildIndex(options.usage);
- }
- do {
- chunk = readRulesChunk(inputRules, Boolean(specialComments));
- compressChunk(chunk.stylesheet, firstAtrulesAllowed, usageData, chunkNum++, logger);
- // structure optimisations
- if (restructuring) {
- restructureBlock(chunk.stylesheet, usageData, logger);
- }
- chunkRules = chunk.stylesheet.rules;
- if (chunk.comment) {
- // add \n before comment if there is another content in outputRules
- if (!outputRules.isEmpty()) {
- outputRules.insert(List.createItem({
- type: 'Raw',
- value: '\n'
- }));
- }
- outputRules.insert(List.createItem(chunk.comment));
- // add \n after comment if chunk is not empty
- if (!chunkRules.isEmpty()) {
- outputRules.insert(List.createItem({
- type: 'Raw',
- value: '\n'
- }));
- }
- }
- if (firstAtrulesAllowed && !chunkRules.isEmpty()) {
- var lastRule = chunkRules.last();
- if (lastRule.type !== 'Atrule' ||
- (lastRule.name !== 'import' && lastRule.name !== 'charset')) {
- firstAtrulesAllowed = false;
- }
- }
- if (specialComments !== 'exclamation') {
- specialComments = false;
- }
- outputRules.appendList(chunkRules);
- } while (!inputRules.isEmpty());
- return {
- ast: ast
- };
- };
|