diff options
Diffstat (limited to 'node_modules/csso/lib/compress.js')
| -rw-r--r-- | node_modules/csso/lib/compress.js | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/node_modules/csso/lib/compress.js b/node_modules/csso/lib/compress.js new file mode 100644 index 00000000..4c4fb4fc --- /dev/null +++ b/node_modules/csso/lib/compress.js @@ -0,0 +1,196 @@ +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 + }; +}; |
