react-scripts
Version:
Configuration and scripts for Create React App.
169 lines (142 loc) • 4.6 kB
JavaScript
var List = require('../utils/list');
var usageUtils = require('./usage');
var clean = require('./clean');
var compress = require('./compress');
var restructureBlock = require('./restructure');
var walkRules = require('../utils/walk').rules;
function readBlock(stylesheet) {
var buffer = new List();
var nonSpaceTokenInBuffer = false;
var protectedComment;
stylesheet.rules.nextUntil(stylesheet.rules.head, function(node, item, list) {
if (node.type === 'Comment' && node.value.charAt(0) === '!') {
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',
rules: buffer
}
};
}
function compressBlock(ast, usageData, num, logger) {
logger('Compress block #' + num, null, true);
var seed = 1;
ast.firstAtrulesAllowed = ast.firstAtrulesAllowed;
walkRules(ast, function() {
if (!this.stylesheet.id) {
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;
}
module.exports = function compress(ast, options) {
options = options || {};
ast = ast || { type: 'StyleSheet', rules: new List() };
var logger = typeof options.logger === 'function' ? options.logger : Function();
var restructuring =
'restructure' in options ? options.restructure :
'restructuring' in options ? options.restructuring :
true;
var result = new List();
var block;
var firstAtrulesAllowed = true;
var blockNum = 1;
var blockRules;
var blockMode = false;
var usageData = false;
var info = ast.info || null;
if (ast.type !== 'StyleSheet') {
blockMode = true;
ast = {
type: 'StyleSheet',
rules: new List([{
type: 'Ruleset',
selector: {
type: 'Selector',
selectors: new List([{
type: 'SimpleSelector',
sequence: new List([{
type: 'Identifier',
name: 'x'
}])
}])
},
block: ast
}])
};
}
if (options.usage) {
usageData = usageUtils.buildIndex(options.usage);
}
do {
block = readBlock(ast);
// console.log(JSON.stringify(block.stylesheet, null, 2));
block.stylesheet.firstAtrulesAllowed = firstAtrulesAllowed;
block.stylesheet = compressBlock(block.stylesheet, usageData, blockNum++, logger);
// structure optimisations
if (restructuring) {
restructureBlock(block.stylesheet, usageData, logger);
}
blockRules = block.stylesheet.rules;
if (block.comment) {
// add \n before comment if there is another content in result
if (!result.isEmpty()) {
result.insert(List.createItem({
type: 'Raw',
value: '\n'
}));
}
result.insert(List.createItem(block.comment));
// add \n after comment if block is not empty
if (!blockRules.isEmpty()) {
result.insert(List.createItem({
type: 'Raw',
value: '\n'
}));
}
}
if (firstAtrulesAllowed && !blockRules.isEmpty()) {
var lastRule = blockRules.last();
if (lastRule.type !== 'Atrule' ||
(lastRule.name !== 'import' && lastRule.name !== 'charset')) {
firstAtrulesAllowed = false;
}
}
result.appendList(blockRules);
} while (!ast.rules.isEmpty());
if (blockMode) {
result = !result.isEmpty() ? result.first().block : {
type: 'Block',
info: info,
declarations: new List()
};
} else {
result = {
type: 'StyleSheet',
info: info,
rules: result
};
}
return {
ast: result
};
};