Spaces:
Configuration error
Configuration error
import { List, clone, walk } from 'css-tree'; | |
import { buildIndex } from './usage.js'; | |
import clean from './clean/index.js'; | |
import replace from './replace/index.js'; | |
import restructure from './restructure/index.js'; | |
function readChunk(input, specialComments) { | |
const children = new List(); | |
let nonSpaceTokenInBuffer = false; | |
let protectedComment; | |
input.nextUntil(input.head, (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; | |
} | |
children.insert(list.remove(item)); | |
}); | |
return { | |
comment: protectedComment, | |
stylesheet: { | |
type: 'StyleSheet', | |
loc: null, | |
children | |
} | |
}; | |
} | |
function compressChunk(ast, firstAtrulesAllowed, num, options) { | |
options.logger(`Compress block #${num}`, null, true); | |
let seed = 1; | |
if (ast.type === 'StyleSheet') { | |
ast.firstAtrulesAllowed = firstAtrulesAllowed; | |
ast.id = seed++; | |
} | |
walk(ast, { | |
visit: 'Atrule', | |
enter(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) { | |
let 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) { | |
if ('restructure' in options) { | |
return options.restructure; | |
} | |
return '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 | |
}); | |
} | |
export default function compress(ast, options) { | |
ast = ast || { type: 'StyleSheet', loc: null, children: new List() }; | |
options = options || {}; | |
const compressOptions = { | |
logger: typeof options.logger === 'function' ? options.logger : function() {}, | |
restructuring: getRestructureOption(options), | |
forceMediaMerge: Boolean(options.forceMediaMerge), | |
usage: options.usage ? buildIndex(options.usage) : false | |
}; | |
const output = new List(); | |
let specialComments = getCommentsOption(options); | |
let firstAtrulesAllowed = true; | |
let input; | |
let chunk; | |
let chunkNum = 1; | |
let 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) { | |
const 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 | |
}; | |
}; | |