Socket
Socket
Sign inDemoInstall

csso

Package Overview
Dependencies
3
Maintainers
3
Versions
82
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.2.0 to 5.0.0

cjs/clean/Atrule.cjs

13

lib/clean/Atrule.js

@@ -1,5 +0,5 @@

var resolveKeyword = require('css-tree').keyword;
var { hasNoChildren } = require('./utils');
import { keyword as resolveKeyword } from 'css-tree';
import { hasNoChildren } from './utils.js';
module.exports = function cleanAtrule(node, item, list) {
export default function cleanAtrule(node, item, list) {
if (node.block) {

@@ -49,2 +49,3 @@ // otherwise removed at-rule don't prevent @import for removal

list.remove(item);
return true;

@@ -55,4 +56,5 @@ }, this);

default:
var name = resolveKeyword(node.name).basename;
default: {
const name = resolveKeyword(node.name).basename;
if (name === 'keyframes' ||

@@ -67,3 +69,4 @@ name === 'media' ||

}
}
}
};

@@ -1,3 +0,3 @@

module.exports = function cleanComment(data, item, list) {
export default function cleanComment(data, item, list) {
list.remove(item);
};

@@ -1,5 +0,5 @@

var property = require('css-tree').property;
import { property } from 'css-tree';
module.exports = function cleanDeclartion(node, item, list) {
if (node.value.children && node.value.children.isEmpty()) {
export default function cleanDeclartion(node, item, list) {
if (node.value.children && node.value.children.isEmpty) {
list.remove(item);

@@ -6,0 +6,0 @@ return;

@@ -1,15 +0,23 @@

var walk = require('css-tree').walk;
var handlers = {
Atrule: require('./Atrule'),
Comment: require('./Comment'),
Declaration: require('./Declaration'),
Raw: require('./Raw'),
Rule: require('./Rule'),
TypeSelector: require('./TypeSelector'),
WhiteSpace: require('./WhiteSpace')
import { walk } from 'css-tree';
import Atrule from './Atrule.js';
import Comment from './Comment.js';
import Declaration from './Declaration.js';
import Raw from './Raw.js';
import Rule from './Rule.js';
import TypeSelector from './TypeSelector.js';
import WhiteSpace from './WhiteSpace.js';
const handlers = {
Atrule,
Comment,
Declaration,
Raw,
Rule,
TypeSelector,
WhiteSpace
};
module.exports = function(ast, options) {
export default function(ast, options) {
walk(ast, {
leave: function(node, item, list) {
leave(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {

@@ -16,0 +24,0 @@ handlers[node.type].call(this, node, item, list, options);

@@ -1,4 +0,4 @@

var { isNodeChildrenList } = require('./utils');
import { isNodeChildrenList } from './utils.js';
module.exports = function cleanRaw(node, item, list) {
export default function cleanRaw(node, item, list) {
// raw in stylesheet or block children

@@ -5,0 +5,0 @@ if (isNodeChildrenList(this.stylesheet, list) ||

@@ -1,8 +0,9 @@

var hasOwnProperty = Object.prototype.hasOwnProperty;
var walk = require('css-tree').walk;
var { hasNoChildren } = require('./utils');
import { walk } from 'css-tree';
import { hasNoChildren } from './utils.js';
const { hasOwnProperty } = Object.prototype;
function cleanUnused(selectorList, usageData) {
selectorList.children.each(function(selector, item, list) {
var shouldRemove = false;
selectorList.children.forEach((selector, item, list) => {
let shouldRemove = false;

@@ -74,6 +75,6 @@ walk(selector, function(node) {

return selectorList.children.isEmpty();
return selectorList.children.isEmpty;
}
module.exports = function cleanRule(node, item, list, options) {
export default function cleanRule(node, item, list, options) {
if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) {

@@ -84,6 +85,6 @@ list.remove(item);

var usageData = options.usage;
const { usage } = options;
if (usageData && (usageData.whitelist !== null || usageData.blacklist !== null)) {
cleanUnused(node.prelude, usageData);
if (usage && (usage.whitelist !== null || usage.blacklist !== null)) {
cleanUnused(node.prelude, usage);

@@ -90,0 +91,0 @@ if (hasNoChildren(node.prelude)) {

// remove useless universal selector
module.exports = function cleanTypeSelector(node, item, list) {
var name = item.data.name;
export default function cleanTypeSelector(node, item, list) {
const name = item.data.name;

@@ -11,3 +11,3 @@ // check it's a non-namespaced universal selector

// remove when universal selector before other selectors
var nextType = item.next && item.next.data.type;
const nextType = item.next && item.next.data.type;
if (nextType === 'IdSelector' ||

@@ -14,0 +14,0 @@ nextType === 'ClassSelector' ||

@@ -1,8 +0,7 @@

module.exports = {
hasNoChildren: function(node) {
return !node || !node.children || node.children.isEmpty();
},
isNodeChildrenList: function(node, list) {
return node !== null && node.children === list;
}
};
export function hasNoChildren(node) {
return !node || !node.children || node.children.isEmpty;
}
export function isNodeChildrenList(node, list) {
return node !== null && node.children === list;
}

@@ -1,30 +0,3 @@

var { isNodeChildrenList } = require('./utils');
function isSafeOperator(node) {
return node.type === 'Operator' && node.value !== '+' && node.value !== '-';
}
module.exports = function cleanWhitespace(node, item, list) {
// remove when first or last item in sequence
if (item.next === null || item.prev === null) {
list.remove(item);
return;
}
// white space in stylesheet or block children
if (isNodeChildrenList(this.stylesheet, list) ||
isNodeChildrenList(this.block, list)) {
list.remove(item);
return;
}
if (item.next.data.type === 'WhiteSpace') {
list.remove(item);
return;
}
if (isSafeOperator(item.prev.data) || isSafeOperator(item.next.data)) {
list.remove(item);
return;
}
export default function cleanWhitespace(node, item, list) {
list.remove(item);
};

@@ -1,15 +0,13 @@

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;
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(children, specialComments) {
var buffer = new List();
var nonSpaceTokenInBuffer = false;
var protectedComment;
function readChunk(input, specialComments) {
const children = new List();
let nonSpaceTokenInBuffer = false;
let protectedComment;
children.nextUntil(children.head, function(node, item, list) {
input.nextUntil(input.head, (node, item, list) => {
if (node.type === 'Comment') {

@@ -27,2 +25,3 @@ if (!specialComments || node.value.charAt(0) !== '!') {

protectedComment = node;
return;

@@ -35,3 +34,3 @@ }

buffer.insert(list.remove(item));
children.insert(list.remove(item));
});

@@ -44,3 +43,3 @@

loc: null,
children: buffer
children
}

@@ -51,5 +50,5 @@ };

function compressChunk(ast, firstAtrulesAllowed, num, options) {
options.logger('Compress block #' + num, null, true);
options.logger(`Compress block #${num}`, null, true);
var seed = 1;
let seed = 1;

@@ -63,3 +62,3 @@ if (ast.type === 'StyleSheet') {

visit: 'Atrule',
enter: function markScopes(node) {
enter(node) {
if (node.block !== null) {

@@ -89,3 +88,3 @@ node.block.id = seed++;

function getCommentsOption(options) {
var comments = 'comments' in options ? options.comments : 'exclamation';
let comments = 'comments' in options ? options.comments : 'exclamation';

@@ -126,23 +125,23 @@ if (typeof comments === 'boolean') {

},
block: block
block
});
}
module.exports = function compress(ast, options) {
export default function compress(ast, options) {
ast = ast || { type: 'StyleSheet', loc: null, children: new List() };
options = options || {};
var compressOptions = {
const compressOptions = {
logger: typeof options.logger === 'function' ? options.logger : function() {},
restructuring: getRestructureOption(options),
forceMediaMerge: Boolean(options.forceMediaMerge),
usage: options.usage ? usageUtils.buildIndex(options.usage) : false
usage: options.usage ? buildIndex(options.usage) : false
};
var specialComments = getCommentsOption(options);
var firstAtrulesAllowed = true;
var input;
var output = new List();
var chunk;
var chunkNum = 1;
var chunkChildren;
const output = new List();
let specialComments = getCommentsOption(options);
let firstAtrulesAllowed = true;
let input;
let chunk;
let chunkNum = 1;
let chunkChildren;

@@ -167,3 +166,3 @@ if (options.clone) {

// add \n before comment if there is another content in output
if (!output.isEmpty()) {
if (!output.isEmpty) {
output.insert(List.createItem({

@@ -178,3 +177,3 @@ type: 'Raw',

// add \n after comment if chunk is not empty
if (!chunkChildren.isEmpty()) {
if (!chunkChildren.isEmpty) {
output.insert(List.createItem({

@@ -187,4 +186,4 @@ type: 'Raw',

if (firstAtrulesAllowed && !chunkChildren.isEmpty()) {
var lastRule = chunkChildren.last();
if (firstAtrulesAllowed && !chunkChildren.isEmpty) {
const lastRule = chunkChildren.last;

@@ -202,7 +201,7 @@ if (lastRule.type !== 'Atrule' ||

output.appendList(chunkChildren);
} while (!input.isEmpty());
} while (!input.isEmpty);
return {
ast: ast
ast
};
};

@@ -1,9 +0,10 @@

var csstree = require('css-tree');
var parse = csstree.parse;
var compress = require('./compress');
var generate = csstree.generate;
import { syntax } from './syntax.js';
import { version } from './version.js';
import * as utils from './utils.js';
const { parse, generate, compress } = syntax;
function debugOutput(name, options, startTime, data) {
if (options.debug) {
console.error('## ' + name + ' done in %d ms\n', Date.now() - startTime);
console.error(`## ${name} done in %d ms\n`, Date.now() - startTime);
}

@@ -15,13 +16,13 @@

function createDefaultLogger(level) {
var lastDebug;
let lastDebug;
return function logger(title, ast) {
var line = title;
let line = title;
if (ast) {
line = '[' + ((Date.now() - lastDebug) / 1000).toFixed(3) + 's] ' + line;
line = `[${((Date.now() - lastDebug) / 1000).toFixed(3)}s] ${line}`;
}
if (level > 1 && ast) {
var css = generate(ast);
let css = generate(ast);

@@ -33,3 +34,3 @@ // when level 2, limit css to 256 symbols

line += '\n ' + css + '\n';
line += `\n ${css}\n`;
}

@@ -42,14 +43,4 @@

function copy(obj) {
var result = {};
for (var key in obj) {
result[key] = obj[key];
}
return result;
}
function buildCompressOptions(options) {
options = copy(options);
options = { ...options };

@@ -68,5 +59,3 @@ if (typeof options.logger !== 'function' && options.debug) {

handlers.forEach(function(fn) {
fn(ast, options);
});
handlers.forEach(fn => fn(ast, options));
}

@@ -77,10 +66,10 @@

var filename = options.filename || '<unknown>';
var result;
const filename = options.filename || '<unknown>';
let result;
// parse
var ast = debugOutput('parsing', options, Date.now(),
const ast = debugOutput('parsing', options, Date.now(),
parse(source, {
context: context,
filename: filename,
context,
filename,
positions: Boolean(options.sourceMap)

@@ -98,3 +87,3 @@ })

// compress
var compressResult = debugOutput('compress', options, Date.now(),
const compressResult = debugOutput('compress', options, Date.now(),
compress(ast, buildCompressOptions(options))

@@ -112,8 +101,10 @@ );

if (options.sourceMap) {
result = debugOutput('generate(sourceMap: true)', options, Date.now(), (function() {
var tmp = generate(compressResult.ast, { sourceMap: true });
result = debugOutput('generate(sourceMap: true)', options, Date.now(), (() => {
const tmp = generate(compressResult.ast, { sourceMap: true });
tmp.map._file = filename; // since other tools can relay on file in source map transform chain
tmp.map.setSourceContent(filename, source);
return tmp;
}()));
})());
} else {

@@ -137,13 +128,12 @@ result = debugOutput('generate', options, Date.now(), {

module.exports = {
version: require('../package.json').version,
export {
version,
utils,
// main methods
minify: minifyStylesheet,
minifyBlock: minifyBlock,
minifyStylesheet as minify,
minifyBlock,
// css syntax parser/walkers/generator/etc
syntax: Object.assign({
compress: compress
}, csstree)
syntax
};

@@ -1,5 +0,5 @@

var resolveKeyword = require('css-tree').keyword;
var compressKeyframes = require('./atrule/keyframes');
import { keyword as resolveKeyword } from 'css-tree';
import compressKeyframes from './atrule/keyframes.js';
module.exports = function(node) {
export default function(node) {
// compress @keyframe selectors

@@ -6,0 +6,0 @@ if (resolveKeyword(node.name).basename === 'keyframes') {

@@ -1,5 +0,5 @@

module.exports = function(node) {
node.block.children.each(function(rule) {
rule.prelude.children.each(function(simpleselector) {
simpleselector.children.each(function(data, item) {
export default function(node) {
node.block.children.forEach((rule) => {
rule.prelude.children.forEach((simpleselector) => {
simpleselector.children.forEach((data, item) => {
if (data.type === 'Percentage' && data.value === '100') {

@@ -6,0 +6,0 @@ item.data = {

// Can unquote attribute detection
// Adopted implementation of Mathias Bynens
// https://github.com/mathiasbynens/mothereff.in/blob/master/unquoted-attributes/eff.js
var escapesRx = /\\([0-9A-Fa-f]{1,6})(\r\n|[ \t\n\f\r])?|\\./g;
var blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
const blockUnquoteRx = /^(-?\d|--)|[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/;
function canUnquote(value) {
if (value === '' || value === '-') {
return;
return false;
}
// Escapes are valid, so replace them with a valid non-empty string
value = value.replace(escapesRx, 'a');
return !blockUnquoteRx.test(value);
}
module.exports = function(node) {
var attrValue = node.value;
export default function(node) {
const attrValue = node.value;

@@ -25,10 +21,9 @@ if (!attrValue || attrValue.type !== 'String') {

var unquotedValue = attrValue.value.replace(/^(.)(.*)\1$/, '$2');
if (canUnquote(unquotedValue)) {
if (canUnquote(attrValue.value)) {
node.value = {
type: 'Identifier',
loc: attrValue.loc,
name: unquotedValue
name: attrValue.value
};
}
};

@@ -1,6 +0,6 @@

var lexer = require('css-tree').lexer;
var packNumber = require('./Number').pack;
import { lexer } from 'css-tree';
import { packNumber } from './Number.js';
// http://www.w3.org/TR/css3-color/#svg-color
var NAME_TO_HEX = {
const NAME_TO_HEX = {
'aliceblue': 'f0f8ff',

@@ -156,3 +156,3 @@ 'antiquewhite': 'faebd7',

var HEX_TO_NAME = {
const HEX_TO_NAME = {
'800000': 'maroon',

@@ -218,5 +218,5 @@ '800080': 'purple',

function hslToRgb(h, s, l, a) {
var r;
var g;
var b;
let r;
let g;
let b;

@@ -226,4 +226,4 @@ if (s === 0) {

} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;

@@ -245,2 +245,3 @@ r = hueToRgb(p, q, h + 1 / 3);

value = value.toString(16);
return value.length === 1 ? '0' + value : value;

@@ -250,9 +251,8 @@ }

function parseFunctionArgs(functionArgs, count, rgb) {
var cursor = functionArgs.head;
var args = [];
var wasValue = false;
let cursor = functionArgs.head;
let args = [];
let wasValue = false;
while (cursor !== null) {
var node = cursor.data;
var type = node.type;
const { type, value } = cursor.data;

@@ -268,9 +268,10 @@ switch (type) {

args.push({
type: type,
value: Number(node.value)
type,
value: Number(value)
});
break;
case 'Operator':
if (node.value === ',') {
if (value === ',') {
if (!wasValue) {

@@ -280,5 +281,6 @@ return;

wasValue = false;
} else if (wasValue || node.value !== '+') {
} else if (wasValue || value !== '+') {
return;
}
break;

@@ -329,3 +331,3 @@

return args.map(function(arg) {
var value = Math.max(0, arg.value);
let value = Math.max(0, arg.value);

@@ -362,5 +364,5 @@ switch (arg.type) {

function compressFunction(node, item, list) {
var functionName = node.name;
var args;
export function compressFunction(node, item) {
let functionName = node.name;
let args;

@@ -376,3 +378,3 @@ if (functionName === 'rgba' || functionName === 'hsla') {

if (functionName === 'hsla') {
args = hslToRgb.apply(null, args);
args = hslToRgb(...args);
node.name = 'rgba';

@@ -386,3 +388,4 @@ }

// http://stackoverflow.com/questions/11829410/css3-gradient-rendering-issues-from-transparent-to-white
var scopeFunctionName = this.function && this.function.name;
const scopeFunctionName = this.function && this.function.name;
if ((args[0] === 0 && args[1] === 0 && args[2] === 0) ||

@@ -403,3 +406,3 @@ !/^(?:to|from|color-stop)$|gradient$/i.test(scopeFunctionName)) {

// replace argument values for normalized/interpolated
node.children.each(function(node, item, list) {
node.children.forEach((node, item, list) => {
if (node.type === 'Operator') {

@@ -415,3 +418,3 @@ if (node.value !== ',') {

loc: node.loc,
value: packNumber(args.shift(), null)
value: packNumber(args.shift())
};

@@ -436,3 +439,3 @@ });

// convert to rgb
args = hslToRgb.apply(null, args);
args = hslToRgb(...args);
functionName = 'rgb';

@@ -449,11 +452,2 @@ }

// check if color is not at the end and not followed by space
var next = item.next;
if (next && next.data.type !== 'WhiteSpace') {
list.insert(list.createItem({
type: 'WhiteSpace',
value: ' '
}), next);
}
item.data = {

@@ -469,3 +463,3 @@ type: 'Hash',

function compressIdent(node, item) {
export function compressIdent(node, item) {
if (this.declaration === null) {

@@ -475,7 +469,7 @@ return;

var color = node.name.toLowerCase();
let color = node.name.toLowerCase();
if (NAME_TO_HEX.hasOwnProperty(color) &&
lexer.matchDeclaration(this.declaration).isType(node, 'color')) {
var hex = NAME_TO_HEX[color];
const hex = NAME_TO_HEX[color];

@@ -501,4 +495,4 @@ if (hex.length + 1 <= color.length) {

function compressHex(node, item) {
var color = node.value.toLowerCase();
export function compressHex(node, item) {
let color = node.value.toLowerCase();

@@ -523,7 +517,1 @@ // #112233 -> #123

}
module.exports = {
compressFunction: compressFunction,
compressIdent: compressIdent,
compressHex: compressHex
};

@@ -1,33 +0,34 @@

var packNumber = require('./Number').pack;
var MATH_FUNCTIONS = {
'calc': true,
'min': true,
'max': true,
'clamp': true
};
var LENGTH_UNIT = {
import { packNumber } from './Number.js';
const MATH_FUNCTIONS = new Set([
'calc',
'min',
'max',
'clamp'
]);
const LENGTH_UNIT = new Set([
// absolute length units
'px': true,
'mm': true,
'cm': true,
'in': true,
'pt': true,
'pc': true,
'px',
'mm',
'cm',
'in',
'pt',
'pc',
// relative length units
'em': true,
'ex': true,
'ch': true,
'rem': true,
'em',
'ex',
'ch',
'rem',
// viewport-percentage lengths
'vh': true,
'vw': true,
'vmin': true,
'vmax': true,
'vm': true
};
'vh',
'vw',
'vmin',
'vmax',
'vm'
]);
module.exports = function compressDimension(node, item) {
var value = packNumber(node.value, item);
export default function compressDimension(node, item) {
const value = packNumber(node.value);

@@ -37,6 +38,6 @@ node.value = value;

if (value === '0' && this.declaration !== null && this.atrulePrelude === null) {
var unit = node.unit.toLowerCase();
const unit = node.unit.toLowerCase();
// only length values can be compressed
if (!LENGTH_UNIT.hasOwnProperty(unit)) {
if (!LENGTH_UNIT.has(unit)) {
return;

@@ -53,3 +54,3 @@ }

// issue #222: don't remove units inside calc
if (this.function && MATH_FUNCTIONS.hasOwnProperty(this.function.name)) {
if (this.function && MATH_FUNCTIONS.has(this.function.name)) {
return;

@@ -61,5 +62,5 @@ }

loc: node.loc,
value: value
value
};
}
};

@@ -1,19 +0,27 @@

var walk = require('css-tree').walk;
var handlers = {
Atrule: require('./Atrule'),
AttributeSelector: require('./AttributeSelector'),
Value: require('./Value'),
Dimension: require('./Dimension'),
Percentage: require('./Percentage'),
Number: require('./Number'),
String: require('./String'),
Url: require('./Url'),
Hash: require('./color').compressHex,
Identifier: require('./color').compressIdent,
Function: require('./color').compressFunction
import { walk } from 'css-tree';
import Atrule from './Atrule.js';
import AttributeSelector from './AttributeSelector.js';
import Value from './Value.js';
import Dimension from './Dimension.js';
import Percentage from './Percentage.js';
import { Number } from './Number.js';
import Url from './Url.js';
import { compressHex, compressIdent, compressFunction } from './color.js';
const handlers = {
Atrule,
AttributeSelector,
Value,
Dimension,
Percentage,
Number,
Url,
Hash: compressHex,
Identifier: compressIdent,
Function: compressFunction
};
module.exports = function(ast) {
export default function(ast) {
walk(ast, {
leave: function(node, item, list) {
leave(node, item, list) {
if (handlers.hasOwnProperty(node.type)) {

@@ -20,0 +28,0 @@ handlers[node.type].call(this, node, item, list);

@@ -1,15 +0,15 @@

var OMIT_PLUSSIGN = /^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
var KEEP_PLUSSIGN = /^([\+\-])?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
var unsafeToRemovePlusSignAfter = {
Dimension: true,
Hash: true,
Identifier: true,
Number: true,
Raw: true,
UnicodeRange: true
};
const OMIT_PLUSSIGN = /^(?:\+|(-))?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
const KEEP_PLUSSIGN = /^([\+\-])?0*(\d*)(?:\.0*|(\.\d*?)0*)?$/;
const unsafeToRemovePlusSignAfter = new Set([
'Dimension',
'Hash',
'Identifier',
'Number',
'Raw',
'UnicodeRange'
]);
function packNumber(value, item) {
export function packNumber(value, item) {
// omit plus sign only if no prev or prev is safe type
var regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.hasOwnProperty(item.prev.data.type)
const regexp = item && item.prev !== null && unsafeToRemovePlusSignAfter.has(item.prev.data.type)
? KEEP_PLUSSIGN

@@ -20,3 +20,3 @@ : OMIT_PLUSSIGN;

// 00100 -> '100'
// +100 -> '100' (only when safe, e.g. omitting plus sign for 1px+1px leads to single dimension instead of two)
// +100 -> '100'
// -100 -> '-100'

@@ -33,2 +33,4 @@ // 0.123 -> '.123'

}
// FIXME: is it solution simplier?
// value = String(Number(value)).replace(/^(-?)0+\./, '$1.');

@@ -38,5 +40,4 @@ return value;

module.exports = function(node, item) {
node.value = packNumber(node.value, item);
export function Number(node) {
node.value = packNumber(node.value);
};
module.exports.pack = packNumber;

@@ -1,4 +0,5 @@

var lexer = require('css-tree').lexer;
var packNumber = require('./Number').pack;
var blacklist = new Set([
import { lexer } from 'css-tree';
import { packNumber } from './Number.js';
const blacklist = new Set([
// see https://github.com/jakubpawlowicz/clean-css/issues/957

@@ -19,4 +20,4 @@ 'width',

module.exports = function compressPercentage(node, item) {
node.value = packNumber(node.value, item);
export default function compressPercentage(node, item) {
node.value = packNumber(node.value);

@@ -23,0 +24,0 @@ if (node.value === '0' && this.declaration && !blacklist.has(this.declaration.property)) {

@@ -1,15 +0,5 @@

var List = require('css-tree').List;
import { List } from 'css-tree';
module.exports = function compressBackground(node) {
function lastType() {
if (buffer.length) {
return buffer[buffer.length - 1].type;
}
}
export default function compressBackground(node) {
function flush() {
if (lastType() === 'WhiteSpace') {
buffer.pop();
}
if (!buffer.length) {

@@ -23,6 +13,2 @@ buffer.unshift(

{
type: 'WhiteSpace',
value: ' '
},
{
type: 'Number',

@@ -40,6 +26,6 @@ loc: null,

var newValue = [];
var buffer = [];
let newValue = [];
let buffer = [];
node.children.each(function(node) {
node.children.forEach((node) => {
if (node.type === 'Operator' && node.value === ',') {

@@ -61,7 +47,2 @@ flush();

// don't add redundant spaces
if (node.type === 'WhiteSpace' && (!buffer.length || lastType() === 'WhiteSpace')) {
return;
}
buffer.push(node);

@@ -68,0 +49,0 @@ });

@@ -1,18 +0,3 @@

function removeItemAndRedundantWhiteSpace(list, item) {
var prev = item.prev;
var next = item.next;
if (next !== null) {
if (next.data.type === 'WhiteSpace' && (prev === null || prev.data.type === 'WhiteSpace')) {
list.remove(next);
}
} else if (prev !== null && prev.data.type === 'WhiteSpace') {
list.remove(prev);
}
list.remove(item);
}
module.exports = function compressBorder(node) {
node.children.each(function(node, item, list) {
export default function compressBorder(node) {
node.children.forEach((node, item, list) => {
if (node.type === 'Identifier' && node.name.toLowerCase() === 'none') {

@@ -27,3 +12,3 @@ if (list.head === list.tail) {

} else {
removeItemAndRedundantWhiteSpace(list, item);
list.remove(item);
}

@@ -30,0 +15,0 @@ }

@@ -1,3 +0,3 @@

module.exports = function compressFontWeight(node) {
var value = node.children.head.data;
export default function compressFontWeight(node) {
const value = node.children.head.data;

@@ -4,0 +4,0 @@ if (value.type === 'Identifier') {

@@ -1,5 +0,5 @@

module.exports = function compressFont(node) {
var list = node.children;
export default function compressFont(node) {
const list = node.children;
list.eachRight(function(node, item) {
list.forEachRight(function(node, item) {
if (node.type === 'Identifier') {

@@ -13,3 +13,3 @@ if (node.name === 'bold') {

} else if (node.name === 'normal') {
var prev = item.prev;
const prev = item.prev;

@@ -22,3 +22,3 @@ if (prev && prev.data.type === 'Operator' && prev.data.value === '/') {

} else if (node.name === 'medium') {
var next = item.next;
const next = item.next;

@@ -32,12 +32,3 @@ if (!next || next.data.type !== 'Operator') {

// remove redundant spaces
list.each(function(node, item) {
if (node.type === 'WhiteSpace') {
if (!item.prev || !item.next || item.next.data.type === 'WhiteSpace') {
this.remove(item);
}
}
});
if (list.isEmpty()) {
if (list.isEmpty) {
list.insert(list.createItem({

@@ -44,0 +35,0 @@ type: 'Identifier',

@@ -1,33 +0,4 @@

var UNICODE = '\\\\[0-9a-f]{1,6}(\\r\\n|[ \\n\\r\\t\\f])?';
var ESCAPE = '(' + UNICODE + '|\\\\[^\\n\\r\\f0-9a-fA-F])';
var NONPRINTABLE = '\u0000\u0008\u000b\u000e-\u001f\u007f';
var SAFE_URL = new RegExp('^(' + ESCAPE + '|[^\"\'\\(\\)\\\\\\s' + NONPRINTABLE + '])*$', 'i');
module.exports = function(node) {
var value = node.value;
if (value.type !== 'String') {
return;
}
var quote = value.value[0];
var url = value.value.substr(1, value.value.length - 2);
export default function(node) {
// convert `\\` to `/`
url = url.replace(/\\\\/g, '/');
// remove quotes when safe
// https://www.w3.org/TR/css-syntax-3/#url-unquoted-diagram
if (SAFE_URL.test(url)) {
node.value = {
type: 'Raw',
loc: node.value.loc,
value: url
};
} else {
// use double quotes if string has no double quotes
// otherwise use original quotes
// TODO: make better quote type selection
node.value.value = url.indexOf('"') === -1 ? '"' + url + '"' : quote + url + quote;
}
node.value = node.value.replace(/\\/g, '/');
};

@@ -1,11 +0,17 @@

var resolveName = require('css-tree').property;
var handlers = {
'font': require('./property/font'),
'font-weight': require('./property/font-weight'),
'background': require('./property/background'),
'border': require('./property/border'),
'outline': require('./property/border')
import { property as resolveName } from 'css-tree';
import font from './property/font.js';
import fontWeight from './property/font-weight.js';
import background from './property/background.js';
import border from './property/border.js';
import outline from './property/border.js';
const handlers = {
'font': font,
'font-weight': fontWeight,
'background': background,
'border': border,
'outline': outline
};
module.exports = function compressValue(node) {
export default function compressValue(node) {
if (!this.declaration) {

@@ -15,3 +21,3 @@ return;

var property = resolveName(this.declaration.property);
const property = resolveName(this.declaration.property);

@@ -18,0 +24,0 @@ if (handlers.hasOwnProperty(property.basename)) {

@@ -1,10 +0,9 @@

var List = require('css-tree').List;
var resolveKeyword = require('css-tree').keyword;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var walk = require('css-tree').walk;
import { List, walk, keyword as resolveKeyword } from 'css-tree';
const { hasOwnProperty } = Object.prototype;
function addRuleToMap(map, item, list, single) {
var node = item.data;
var name = resolveKeyword(node.name).basename;
var id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);
const node = item.data;
const name = resolveKeyword(node.name).basename;
const id = node.name.toLowerCase() + '/' + (node.prelude ? node.prelude.id : null);

@@ -27,8 +26,8 @@ if (!hasOwnProperty.call(map, name)) {

function relocateAtrules(ast, options) {
var collected = Object.create(null);
var topInjectPoint = null;
const collected = Object.create(null);
let topInjectPoint = null;
ast.children.each(function(node, item, list) {
ast.children.forEach(function(node, item, list) {
if (node.type === 'Atrule') {
var name = resolveKeyword(node.name).basename;
const name = resolveKeyword(node.name).basename;

@@ -60,4 +59,4 @@ switch (name) {

for (var atrule in collected) {
for (var id in collected[atrule]) {
for (const atrule in collected) {
for (const id in collected[atrule]) {
ast.children.insertList(

@@ -80,3 +79,3 @@ collected[atrule][id],

var prev = item.prev && item.prev.data;
const prev = item.prev && item.prev.data;

@@ -102,3 +101,3 @@ if (!prev || !isMediaRule(prev)) {

module.exports = function rejoinAtrule(ast, options) {
export default function rejoinAtrule(ast, options) {
relocateAtrules(ast, options);

@@ -105,0 +104,0 @@

@@ -1,7 +0,13 @@

var walk = require('css-tree').walk;
var utils = require('./utils');
import { walk } from 'css-tree';
import {
unsafeToSkipNode,
isEqualSelectors,
isEqualDeclarations,
addSelectors,
hasSimilarSelectors
} from './utils.js';
function processRule(node, item, list) {
var selectors = node.prelude.children;
var declarations = node.block.children;
const selectors = node.prelude.children;
const declarations = node.block.children;

@@ -11,7 +17,7 @@ list.prevUntil(item.prev, function(prev) {

if (prev.type !== 'Rule') {
return utils.unsafeToSkipNode.call(selectors, prev);
return unsafeToSkipNode.call(selectors, prev);
}
var prevSelectors = prev.prelude.children;
var prevDeclarations = prev.block.children;
const prevSelectors = prev.prelude.children;
const prevDeclarations = prev.block.children;

@@ -21,3 +27,3 @@ // try to join rulesets with equal pseudo signature

// try to join by selectors
if (utils.isEqualSelectors(prevSelectors, selectors)) {
if (isEqualSelectors(prevSelectors, selectors)) {
prevDeclarations.appendList(declarations);

@@ -29,4 +35,4 @@ list.remove(item);

// try to join by declarations
if (utils.isEqualDeclarations(declarations, prevDeclarations)) {
utils.addSelectors(prevSelectors, selectors);
if (isEqualDeclarations(declarations, prevDeclarations)) {
addSelectors(prevSelectors, selectors);
list.remove(item);

@@ -38,3 +44,3 @@ return true;

// go to prev ruleset if has no selector similarities
return utils.hasSimilarSelectors(selectors, prevSelectors);
return hasSimilarSelectors(selectors, prevSelectors);
});

@@ -46,3 +52,3 @@ }

// TODO: remove initial merge
module.exports = function initialMergeRule(ast) {
export default function initialMergeRule(ast) {
walk(ast, {

@@ -49,0 +55,0 @@ visit: 'Rule',

@@ -1,6 +0,5 @@

var List = require('css-tree').List;
var walk = require('css-tree').walk;
import { List, walk } from 'css-tree';
function processRule(node, item, list) {
var selectors = node.prelude.children;
const selectors = node.prelude.children;

@@ -15,3 +14,4 @@ // generate new rule sets:

while (selectors.head !== selectors.tail) {
var newSelectors = new List();
const newSelectors = new List();
newSelectors.insert(selectors.remove(selectors.head));

@@ -37,3 +37,3 @@

module.exports = function disjoinRule(ast) {
export default function disjoinRule(ast) {
walk(ast, {

@@ -40,0 +40,0 @@ visit: 'Rule',

@@ -1,13 +0,11 @@

var List = require('css-tree').List;
var generate = require('css-tree').generate;
var walk = require('css-tree').walk;
import { List, generate, walk } from 'css-tree';
var REPLACE = 1;
var REMOVE = 2;
var TOP = 0;
var RIGHT = 1;
var BOTTOM = 2;
var LEFT = 3;
var SIDES = ['top', 'right', 'bottom', 'left'];
var SIDE = {
const REPLACE = 1;
const REMOVE = 2;
const TOP = 0;
const RIGHT = 1;
const BOTTOM = 2;
const LEFT = 3;
const SIDES = ['top', 'right', 'bottom', 'left'];
const SIDE = {
'margin-top': 'top',

@@ -36,3 +34,3 @@ 'margin-right': 'right',

};
var MAIN_PROPERTY = {
const MAIN_PROPERTY = {
'margin': 'margin',

@@ -67,277 +65,272 @@ 'margin-top': 'margin',

function TRBL(name) {
this.name = name;
this.loc = null;
this.iehack = undefined;
this.sides = {
'top': null,
'right': null,
'bottom': null,
'left': null
};
}
class TRBL {
constructor(name) {
this.name = name;
this.loc = null;
this.iehack = undefined;
this.sides = {
'top': null,
'right': null,
'bottom': null,
'left': null
};
}
TRBL.prototype.getValueSequence = function(declaration, count) {
var values = [];
var iehack = '';
var hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
var special = false;
getValueSequence(declaration, count) {
const values = [];
let iehack = '';
const hasBadValues = declaration.value.type !== 'Value' || declaration.value.children.some(function(child) {
let special = false;
switch (child.type) {
case 'Identifier':
switch (child.name) {
case '\\0':
case '\\9':
iehack = child.name;
return;
switch (child.type) {
case 'Identifier':
switch (child.name) {
case '\\0':
case '\\9':
iehack = child.name;
return;
case 'inherit':
case 'initial':
case 'unset':
case 'revert':
special = child.name;
break;
}
break;
case 'inherit':
case 'initial':
case 'unset':
case 'revert':
special = child.name;
break;
}
break;
case 'Dimension':
switch (child.unit) {
// is not supported until IE11
case 'rem':
case 'Dimension':
switch (child.unit) {
// is not supported until IE11
case 'rem':
// v* units is too buggy across browsers and better
// don't merge values with those units
case 'vw':
case 'vh':
case 'vmin':
case 'vmax':
case 'vm': // IE9 supporting "vm" instead of "vmin".
special = child.unit;
break;
}
break;
// v* units is too buggy across browsers and better
// don't merge values with those units
case 'vw':
case 'vh':
case 'vmin':
case 'vmax':
case 'vm': // IE9 supporting "vm" instead of "vmin".
special = child.unit;
break;
}
break;
case 'Hash': // color
case 'Number':
case 'Percentage':
break;
case 'Hash': // color
case 'Number':
case 'Percentage':
break;
case 'Function':
if (child.name === 'var') {
return true;
}
case 'Function':
if (child.name === 'var') {
return true;
}
special = child.name;
break;
special = child.name;
break;
case 'WhiteSpace':
return false; // ignore space
default:
return true; // bad value
}
default:
return true; // bad value
values.push({
node: child,
special,
important: declaration.important
});
});
if (hasBadValues || values.length > count) {
return false;
}
values.push({
node: child,
special: special,
important: declaration.important
});
});
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
return false;
}
if (hasBadValues || values.length > count) {
return false;
this.iehack = iehack; // move outside
return values;
}
if (typeof this.iehack === 'string' && this.iehack !== iehack) {
return false;
canOverride(side, value) {
const currentValue = this.sides[side];
return !currentValue || (value.important && !currentValue.important);
}
this.iehack = iehack; // move outside
add(name, declaration) {
function attemptToAdd() {
const sides = this.sides;
const side = SIDE[name];
return values;
};
if (side) {
if (side in sides === false) {
return false;
}
TRBL.prototype.canOverride = function(side, value) {
var currentValue = this.sides[side];
const values = this.getValueSequence(declaration, 1);
return !currentValue || (value.important && !currentValue.important);
};
if (!values || !values.length) {
return false;
}
TRBL.prototype.add = function(name, declaration) {
function attemptToAdd() {
var sides = this.sides;
var side = SIDE[name];
// can mix only if specials are equal
for (const key in sides) {
if (sides[key] !== null && sides[key].special !== values[0].special) {
return false;
}
}
if (side) {
if (side in sides === false) {
return false;
}
if (!this.canOverride(side, values[0])) {
return true;
}
var values = this.getValueSequence(declaration, 1);
sides[side] = values[0];
if (!values || !values.length) {
return false;
}
return true;
} else if (name === this.name) {
const values = this.getValueSequence(declaration, 4);
// can mix only if specials are equal
for (var key in sides) {
if (sides[key] !== null && sides[key].special !== values[0].special) {
if (!values || !values.length) {
return false;
}
}
if (!this.canOverride(side, values[0])) {
return true;
}
switch (values.length) {
case 1:
values[RIGHT] = values[TOP];
values[BOTTOM] = values[TOP];
values[LEFT] = values[TOP];
break;
sides[side] = values[0];
return true;
} else if (name === this.name) {
var values = this.getValueSequence(declaration, 4);
case 2:
values[BOTTOM] = values[TOP];
values[LEFT] = values[RIGHT];
break;
if (!values || !values.length) {
return false;
}
case 3:
values[LEFT] = values[RIGHT];
break;
}
switch (values.length) {
case 1:
values[RIGHT] = values[TOP];
values[BOTTOM] = values[TOP];
values[LEFT] = values[TOP];
break;
// can mix only if specials are equal
for (let i = 0; i < 4; i++) {
for (const key in sides) {
if (sides[key] !== null && sides[key].special !== values[i].special) {
return false;
}
}
}
case 2:
values[BOTTOM] = values[TOP];
values[LEFT] = values[RIGHT];
break;
case 3:
values[LEFT] = values[RIGHT];
break;
}
// can mix only if specials are equal
for (var i = 0; i < 4; i++) {
for (var key in sides) {
if (sides[key] !== null && sides[key].special !== values[i].special) {
return false;
for (let i = 0; i < 4; i++) {
if (this.canOverride(SIDES[i], values[i])) {
sides[SIDES[i]] = values[i];
}
}
}
for (var i = 0; i < 4; i++) {
if (this.canOverride(SIDES[i], values[i])) {
sides[SIDES[i]] = values[i];
}
return true;
}
}
return true;
if (!attemptToAdd.call(this)) {
return false;
}
}
if (!attemptToAdd.call(this)) {
return false;
}
// TODO: use it when we can refer to several points in source
// if (this.loc) {
// this.loc = {
// primary: this.loc,
// merged: declaration.loc
// };
// } else {
// this.loc = declaration.loc;
// }
if (!this.loc) {
this.loc = declaration.loc;
}
// TODO: use it when we can refer to several points in source
// if (this.loc) {
// this.loc = {
// primary: this.loc,
// merged: declaration.loc
// };
// } else {
// this.loc = declaration.loc;
// }
if (!this.loc) {
this.loc = declaration.loc;
return true;
}
return true;
};
isOkToMinimize() {
const top = this.sides.top;
const right = this.sides.right;
const bottom = this.sides.bottom;
const left = this.sides.left;
TRBL.prototype.isOkToMinimize = function() {
var top = this.sides.top;
var right = this.sides.right;
var bottom = this.sides.bottom;
var left = this.sides.left;
if (top && right && bottom && left) {
const important =
top.important +
right.important +
bottom.important +
left.important;
if (top && right && bottom && left) {
var important =
top.important +
right.important +
bottom.important +
left.important;
return important === 0 || important === 4;
}
return important === 0 || important === 4;
return false;
}
return false;
};
getValue() {
const result = new List();
const sides = this.sides;
const values = [
sides.top,
sides.right,
sides.bottom,
sides.left
];
const stringValues = [
generate(sides.top.node),
generate(sides.right.node),
generate(sides.bottom.node),
generate(sides.left.node)
];
TRBL.prototype.getValue = function() {
var result = new List();
var sides = this.sides;
var values = [
sides.top,
sides.right,
sides.bottom,
sides.left
];
var stringValues = [
generate(sides.top.node),
generate(sides.right.node),
generate(sides.bottom.node),
generate(sides.left.node)
];
if (stringValues[LEFT] === stringValues[RIGHT]) {
values.pop();
if (stringValues[BOTTOM] === stringValues[TOP]) {
if (stringValues[LEFT] === stringValues[RIGHT]) {
values.pop();
if (stringValues[RIGHT] === stringValues[TOP]) {
if (stringValues[BOTTOM] === stringValues[TOP]) {
values.pop();
if (stringValues[RIGHT] === stringValues[TOP]) {
values.pop();
}
}
}
}
for (var i = 0; i < values.length; i++) {
if (i) {
result.appendData({ type: 'WhiteSpace', value: ' ' });
for (let i = 0; i < values.length; i++) {
result.appendData(values[i].node);
}
result.appendData(values[i].node);
}
if (this.iehack) {
result.appendData({
type: 'Identifier',
loc: null,
name: this.iehack
});
}
if (this.iehack) {
result.appendData({ type: 'WhiteSpace', value: ' ' });
result.appendData({
type: 'Identifier',
return {
type: 'Value',
loc: null,
name: this.iehack
});
children: result
};
}
return {
type: 'Value',
loc: null,
children: result
};
};
getDeclaration() {
return {
type: 'Declaration',
loc: this.loc,
important: this.sides.top.important,
property: this.name,
value: this.getValue()
};
}
}
TRBL.prototype.getDeclaration = function() {
return {
type: 'Declaration',
loc: this.loc,
important: this.sides.top.important,
property: this.name,
value: this.getValue()
};
};
function processRule(rule, shorts, shortDeclarations, lastShortSelector) {
var declarations = rule.block.children;
var selector = rule.prelude.children.first().id;
const declarations = rule.block.children;
const selector = rule.prelude.children.first.id;
rule.block.children.eachRight(function(declaration, item) {
var property = declaration.property;
rule.block.children.forEachRight(function(declaration, item) {
const property = declaration.property;

@@ -348,5 +341,5 @@ if (!MAIN_PROPERTY.hasOwnProperty(property)) {

var key = MAIN_PROPERTY[property];
var shorthand;
var operation;
const key = MAIN_PROPERTY[property];
let shorthand;
let operation;

@@ -373,6 +366,6 @@ if (!lastShortSelector || selector === lastShortSelector) {

shortDeclarations.push({
operation: operation,
operation,
block: declarations,
item: item,
shorthand: shorthand
item,
shorthand
});

@@ -388,3 +381,3 @@

shortDeclarations.forEach(function(item) {
var shorthand = item.shorthand;
const shorthand = item.shorthand;

@@ -403,5 +396,5 @@ if (!shorthand.isOkToMinimize()) {

module.exports = function restructBlock(ast, indexer) {
var stylesheetMap = {};
var shortDeclarations = [];
export default function restructBlock(ast, indexer) {
const stylesheetMap = {};
const shortDeclarations = [];

@@ -411,7 +404,7 @@ walk(ast, {

reverse: true,
enter: function(node) {
var stylesheet = this.block || this.stylesheet;
var ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first().id;
var ruleMap;
var shorts;
enter(node) {
const stylesheet = this.block || this.stylesheet;
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
let ruleMap;
let shorts;

@@ -418,0 +411,0 @@ if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {

@@ -1,11 +0,14 @@

var resolveProperty = require('css-tree').property;
var resolveKeyword = require('css-tree').keyword;
var walk = require('css-tree').walk;
var generate = require('css-tree').generate;
var fingerprintId = 1;
var dontRestructure = {
'src': 1 // https://github.com/afelix/csso/issues/50
};
import {
walk,
generate,
property as resolveProperty,
keyword as resolveKeyword
} from 'css-tree';
var DONT_MIX_VALUE = {
let fingerprintId = 1;
const dontRestructure = new Set([
'src' // https://github.com/afelix/csso/issues/50
]);
const DONT_MIX_VALUE = {
// https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility

@@ -17,3 +20,3 @@ 'display': /table|ruby|flex|-(flex)?box$|grid|contents|run-in/i,

var SAFE_VALUES = {
const SAFE_VALUES = {
cursor: [

@@ -34,3 +37,3 @@ 'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',

var NEEDLESS_TABLE = {
const NEEDLESS_TABLE = {
'border-width': ['border'],

@@ -74,3 +77,3 @@ 'border-style': ['border'],

function getPropertyFingerprint(propertyName, declaration, fingerprints) {
var realName = resolveProperty(propertyName).basename;
const realName = resolveProperty(propertyName).basename;

@@ -81,4 +84,4 @@ if (realName === 'background') {

var declarationId = declaration.id;
var fingerprint = fingerprints[declarationId];
const declarationId = declaration.id;
let fingerprint = fingerprints[declarationId];

@@ -88,8 +91,8 @@ if (!fingerprint) {

case 'Value':
var vendorId = '';
var iehack = '';
var special = {};
var raw = false;
const special = {};
let vendorId = '';
let iehack = '';
let raw = false;
declaration.value.children.each(function walk(node) {
declaration.value.children.forEach(function walk(node) {
switch (node.type) {

@@ -99,3 +102,3 @@ case 'Value':

case 'Parentheses':
node.children.each(walk);
node.children.forEach(walk);
break;

@@ -107,4 +110,4 @@

case 'Identifier':
var name = node.name;
case 'Identifier': {
const { name } = node;

@@ -130,5 +133,6 @@ if (!vendorId) {

break;
}
case 'Function':
var name = node.name;
case 'Function': {
let { name } = node;

@@ -144,5 +148,6 @@ if (!vendorId) {

// only the same form values can be merged
var hasComma = node.children.some(function(node) {
return node.type === 'Operator' && node.value === ',';
});
const hasComma = node.children.some((node) =>
node.type === 'Operator' && node.value === ','
);
if (!hasComma) {

@@ -156,8 +161,9 @@ name = 'rect-backward';

// check nested tokens too
node.children.each(walk);
node.children.forEach(walk);
break;
}
case 'Dimension':
var unit = node.unit;
case 'Dimension': {
const { unit } = node;

@@ -182,3 +188,5 @@ if (/\\[09]/.test(unit)) {

}
break;
}
}

@@ -207,10 +215,10 @@ });

function needless(props, declaration, fingerprints) {
var property = resolveProperty(declaration.property);
const property = resolveProperty(declaration.property);
if (NEEDLESS_TABLE.hasOwnProperty(property.basename)) {
var table = NEEDLESS_TABLE[property.basename];
const table = NEEDLESS_TABLE[property.basename];
for (var i = 0; i < table.length; i++) {
var ppre = getPropertyFingerprint(property.prefix + table[i], declaration, fingerprints);
var prev = props.hasOwnProperty(ppre) ? props[ppre] : null;
for (const entry of table) {
const ppre = getPropertyFingerprint(property.prefix + entry, declaration, fingerprints);
const prev = props.hasOwnProperty(ppre) ? props[ppre] : null;

@@ -225,10 +233,10 @@ if (prev && (!declaration.important || prev.item.data.important)) {

function processRule(rule, item, list, props, fingerprints) {
var declarations = rule.block.children;
const declarations = rule.block.children;
declarations.eachRight(function(declaration, declarationItem) {
var property = declaration.property;
var fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
var prev = props[fingerprint];
declarations.forEachRight(function(declaration, declarationItem) {
const { property } = declaration;
const fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
const prev = props[fingerprint];
if (prev && !dontRestructure.hasOwnProperty(property)) {
if (prev && !dontRestructure.has(property)) {
if (declaration.important && !prev.item.data.important) {

@@ -257,3 +265,3 @@ props[fingerprint] = {

} else {
var prev = needless(props, declaration, fingerprints);
const prev = needless(props, declaration, fingerprints);

@@ -279,3 +287,3 @@ if (prev) {

if (declarations.isEmpty()) {
if (declarations.isEmpty) {
list.remove(item);

@@ -285,5 +293,5 @@ }

module.exports = function restructBlock(ast) {
var stylesheetMap = {};
var fingerprints = Object.create(null);
export default function restructBlock(ast) {
const stylesheetMap = {};
const fingerprints = Object.create(null);

@@ -293,7 +301,7 @@ walk(ast, {

reverse: true,
enter: function(node, item, list) {
var stylesheet = this.block || this.stylesheet;
var ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first().id;
var ruleMap;
var props;
enter(node, item, list) {
const stylesheet = this.block || this.stylesheet;
const ruleId = (node.pseudoSignature || '') + '|' + node.prelude.children.first.id;
let ruleMap;
let props;

@@ -300,0 +308,0 @@ if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {

@@ -1,3 +0,3 @@

var walk = require('css-tree').walk;
var utils = require('./utils');
import { walk } from 'css-tree';
import { unsafeToSkipNode, isEqualDeclarations} from './utils.js';

@@ -17,6 +17,6 @@ /*

function processRule(node, item, list) {
var selectors = node.prelude.children;
var declarations = node.block.children;
var nodeCompareMarker = selectors.first().compareMarker;
var skippedCompareMarkers = {};
const selectors = node.prelude.children;
const declarations = node.block.children;
const nodeCompareMarker = selectors.first.compareMarker;
const skippedCompareMarkers = {};

@@ -26,3 +26,3 @@ list.nextUntil(item.next, function(next, nextItem) {

if (next.type !== 'Rule') {
return utils.unsafeToSkipNode.call(selectors, next);
return unsafeToSkipNode.call(selectors, next);
}

@@ -34,5 +34,5 @@

var nextFirstSelector = next.prelude.children.head;
var nextDeclarations = next.block.children;
var nextCompareMarker = nextFirstSelector.data.compareMarker;
const nextFirstSelector = next.prelude.children.head;
const nextDeclarations = next.block.children;
const nextCompareMarker = nextFirstSelector.data.compareMarker;

@@ -46,3 +46,3 @@ // if next ruleset has same marked as one of skipped then stop joining

if (selectors.head === selectors.tail) {
if (selectors.first().id === nextFirstSelector.data.id) {
if (selectors.first.id === nextFirstSelector.data.id) {
declarations.appendList(nextDeclarations);

@@ -55,7 +55,7 @@ list.remove(nextItem);

// try to join by properties
if (utils.isEqualDeclarations(declarations, nextDeclarations)) {
var nextStr = nextFirstSelector.data.id;
if (isEqualDeclarations(declarations, nextDeclarations)) {
const nextStr = nextFirstSelector.data.id;
selectors.some(function(data, item) {
var curStr = data.id;
selectors.some((data, item) => {
const curStr = data.id;

@@ -86,3 +86,3 @@ if (nextStr < curStr) {

module.exports = function mergeRule(ast) {
export default function mergeRule(ast) {
walk(ast, {

@@ -89,0 +89,0 @@ visit: 'Rule',

@@ -1,20 +0,18 @@

var List = require('css-tree').List;
var walk = require('css-tree').walk;
var utils = require('./utils');
import { List, walk } from 'css-tree';
import {
unsafeToSkipNode,
isEqualSelectors,
compareDeclarations,
addSelectors
} from './utils.js';
function calcSelectorLength(list) {
var length = 0;
list.each(function(data) {
length += data.id.length + 1;
});
return length - 1;
return list.reduce((res, data) => res + data.id.length + 1, 0) - 1;
}
function calcDeclarationsLength(tokens) {
var length = 0;
let length = 0;
for (var i = 0; i < tokens.length; i++) {
length += tokens[i].length;
for (const token of tokens) {
length += token.length;
}

@@ -29,15 +27,15 @@

function processRule(node, item, list) {
var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
var selectors = node.prelude.children;
var block = node.block;
var disallowDownMarkers = Object.create(null);
var allowMergeUp = true;
var allowMergeDown = true;
const avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
const selectors = node.prelude.children;
const block = node.block;
const disallowDownMarkers = Object.create(null);
let allowMergeUp = true;
let allowMergeDown = true;
list.prevUntil(item.prev, function(prev, prevItem) {
var prevBlock = prev.block;
var prevType = prev.type;
const prevBlock = prev.block;
const prevType = prev.type;
if (prevType !== 'Rule') {
var unsafe = utils.unsafeToSkipNode.call(selectors, prev);
const unsafe = unsafeToSkipNode.call(selectors, prev);

@@ -47,4 +45,4 @@ if (!unsafe && prevType === 'Atrule' && prevBlock) {

visit: 'Rule',
enter: function(node) {
node.prelude.children.each(function(data) {
enter(node) {
node.prelude.children.forEach((data) => {
disallowDownMarkers[data.compareMarker] = true;

@@ -59,4 +57,2 @@ });

var prevSelectors = prev.prelude.children;
if (node.pseudoSignature !== prev.pseudoSignature) {

@@ -66,6 +62,8 @@ return true;

allowMergeDown = !prevSelectors.some(function(selector) {
return selector.compareMarker in disallowDownMarkers;
});
const prevSelectors = prev.prelude.children;
allowMergeDown = !prevSelectors.some((selector) =>
selector.compareMarker in disallowDownMarkers
);
// try prev ruleset if simpleselectors has no equal specifity and element selector

@@ -77,5 +75,6 @@ if (!allowMergeDown && !allowMergeUp) {

// try to join by selectors
if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
if (allowMergeUp && isEqualSelectors(prevSelectors, selectors)) {
prevBlock.children.appendList(block.children);
list.remove(item);
return true;

@@ -85,3 +84,3 @@ }

// try to join by properties
var diff = utils.compareDeclarations(block.children, prevBlock.children);
const diff = compareDeclarations(block.children, prevBlock.children);

@@ -94,3 +93,3 @@ // console.log(diff.eq, diff.ne1, diff.ne2);

if (allowMergeDown) {
utils.addSelectors(selectors, prevSelectors);
addSelectors(selectors, prevSelectors);
list.remove(prevItem);

@@ -105,17 +104,17 @@ }

// prevBlock is subset block
var selectorLength = calcSelectorLength(selectors);
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
const selectorLength = calcSelectorLength(selectors);
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
if (allowMergeUp && selectorLength < blockLength) {
utils.addSelectors(prevSelectors, selectors);
block.children = new List().fromArray(diff.ne1);
addSelectors(prevSelectors, selectors);
block.children.fromArray(diff.ne1);
}
} else if (!diff.ne1.length && diff.ne2.length) {
// node is subset of prevBlock
var selectorLength = calcSelectorLength(prevSelectors);
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
const selectorLength = calcSelectorLength(prevSelectors);
const blockLength = calcDeclarationsLength(diff.eq); // declarations length
if (allowMergeDown && selectorLength < blockLength) {
utils.addSelectors(selectors, prevSelectors);
prevBlock.children = new List().fromArray(diff.ne2);
addSelectors(selectors, prevSelectors);
prevBlock.children.fromArray(diff.ne2);
}

@@ -125,9 +124,9 @@ } else {

// extract equal block
var newSelector = {
const newSelector = {
type: 'SelectorList',
loc: null,
children: utils.addSelectors(prevSelectors.copy(), selectors)
children: addSelectors(prevSelectors.copy(), selectors)
};
var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
var blockLength = calcDeclarationsLength(diff.eq); // declarations length
const newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
const blockLength = calcDeclarationsLength(diff.eq); // declarations length

@@ -137,3 +136,3 @@ // create new ruleset if declarations length greater than

if (blockLength >= newBlockLength) {
var newItem = list.createItem({
const newItem = list.createItem({
type: 'Rule',

@@ -150,4 +149,4 @@ loc: null,

block.children = new List().fromArray(diff.ne1);
prevBlock.children = new List().fromArray(diff.ne2overrided);
block.children.fromArray(diff.ne1);
prevBlock.children.fromArray(diff.ne2overrided);

@@ -169,10 +168,10 @@ if (allowMergeUp) {

// await property families to find property interception correctly
allowMergeUp = !prevSelectors.some(function(prevSelector) {
return selectors.some(function(selector) {
return selector.compareMarker === prevSelector.compareMarker;
});
});
allowMergeUp = !prevSelectors.some((prevSelector) =>
selectors.some((selector) =>
selector.compareMarker === prevSelector.compareMarker
)
);
}
prevSelectors.each(function(data) {
prevSelectors.forEach((data) => {
disallowDownMarkers[data.compareMarker] = true;

@@ -183,3 +182,3 @@ });

module.exports = function restructRule(ast) {
export default function restructRule(ast) {
walk(ast, {

@@ -186,0 +185,0 @@ visit: 'Rule',

@@ -1,13 +0,13 @@

var prepare = require('./prepare/index');
var mergeAtrule = require('./1-mergeAtrule');
var initialMergeRuleset = require('./2-initialMergeRuleset');
var disjoinRuleset = require('./3-disjoinRuleset');
var restructShorthand = require('./4-restructShorthand');
var restructBlock = require('./6-restructBlock');
var mergeRuleset = require('./7-mergeRuleset');
var restructRuleset = require('./8-restructRuleset');
import prepare from './prepare/index.js';
import mergeAtrule from './1-mergeAtrule.js';
import initialMergeRuleset from './2-initialMergeRuleset.js';
import disjoinRuleset from './3-disjoinRuleset.js';
import restructShorthand from './4-restructShorthand.js';
import restructBlock from './6-restructBlock.js';
import mergeRuleset from './7-mergeRuleset.js';
import restructRuleset from './8-restructRuleset.js';
module.exports = function(ast, options) {
export default function(ast, options) {
// prepare ast for restructing
var indexer = prepare(ast, options);
const indexer = prepare(ast, options);
options.logger('prepare', ast);

@@ -14,0 +14,0 @@

@@ -1,24 +0,24 @@

var generate = require('css-tree').generate;
import { generate } from 'css-tree';
function Index() {
this.seed = 0;
this.map = Object.create(null);
}
class Index {
constructor() {
this.map = new Map();
}
resolve(str) {
let index = this.map.get(str);
Index.prototype.resolve = function(str) {
var index = this.map[str];
if (index === undefined) {
index = this.map.size + 1;
this.map.set(str, index);
}
if (!index) {
index = ++this.seed;
this.map[str] = index;
return index;
}
return index;
};
module.exports = function createDeclarationIndexer() {
var ids = new Index();
export default function createDeclarationIndexer() {
const ids = new Index();
return function markDeclaration(node) {
var id = generate(node);
const id = generate(node);

@@ -25,0 +25,0 @@ node.id = ids.resolve(id);

@@ -1,14 +0,12 @@

var resolveKeyword = require('css-tree').keyword;
var walk = require('css-tree').walk;
var generate = require('css-tree').generate;
var createDeclarationIndexer = require('./createDeclarationIndexer');
var processSelector = require('./processSelector');
import { walk, generate, keyword as resolveKeyword } from 'css-tree';
import createDeclarationIndexer from './createDeclarationIndexer.js';
import processSelector from './processSelector.js';
module.exports = function prepare(ast, options) {
var markDeclaration = createDeclarationIndexer();
export default function prepare(ast, options) {
const markDeclaration = createDeclarationIndexer();
walk(ast, {
visit: 'Rule',
enter: function processRule(node) {
node.block.children.each(markDeclaration);
enter(node) {
node.block.children.forEach(markDeclaration);
processSelector(node, options.usage);

@@ -20,3 +18,3 @@ }

visit: 'Atrule',
enter: function(node) {
enter(node) {
if (node.prelude) {

@@ -32,4 +30,4 @@ node.prelude.id = null; // pre-init property to avoid multiple hidden class for generate

TODO: need to be checked */
node.block.children.each(function(rule) {
rule.prelude.children.each(function(simpleselector) {
node.block.children.forEach(function(rule) {
rule.prelude.children.forEach(function(simpleselector) {
simpleselector.compareMarker = simpleselector.id;

@@ -36,0 +34,0 @@ });

@@ -1,34 +0,33 @@

var generate = require('css-tree').generate;
var specificity = require('./specificity');
import { generate } from 'css-tree';
import specificity from './specificity.js';
var nonFreezePseudoElements = {
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
var nonFreezePseudoClasses = {
'link': true,
'visited': true,
'hover': true,
'active': true,
'first-letter': true,
'first-line': true,
'after': true,
'before': true
};
const nonFreezePseudoElements = new Set([
'first-letter',
'first-line',
'after',
'before'
]);
const nonFreezePseudoClasses = new Set([
'link',
'visited',
'hover',
'active',
'first-letter',
'first-line',
'after',
'before'
]);
module.exports = function freeze(node, usageData) {
var pseudos = Object.create(null);
var hasPseudo = false;
export default function processSelector(node, usageData) {
const pseudos = new Set();
node.prelude.children.each(function(simpleSelector) {
var tagName = '*';
var scope = 0;
node.prelude.children.forEach(function(simpleSelector) {
let tagName = '*';
let scope = 0;
simpleSelector.children.each(function(node) {
simpleSelector.children.forEach(function(node) {
switch (node.type) {
case 'ClassSelector':
if (usageData && usageData.scopes) {
var classScope = usageData.scopes[node.name] || 0;
const classScope = usageData.scopes[node.name] || 0;

@@ -41,21 +40,24 @@ if (scope !== 0 && classScope !== scope) {

}
break;
case 'PseudoClassSelector':
var name = node.name.toLowerCase();
case 'PseudoClassSelector': {
const name = node.name.toLowerCase();
if (!nonFreezePseudoClasses.hasOwnProperty(name)) {
pseudos[':' + name] = true;
hasPseudo = true;
if (!nonFreezePseudoClasses.has(name)) {
pseudos.add(`:${name}`);
}
break;
}
case 'PseudoElementSelector':
var name = node.name.toLowerCase();
case 'PseudoElementSelector': {
const name = node.name.toLowerCase();
if (!nonFreezePseudoElements.hasOwnProperty(name)) {
pseudos['::' + name] = true;
hasPseudo = true;
if (!nonFreezePseudoElements.has(name)) {
pseudos.add(`::${name}`);
}
break;
}

@@ -68,8 +70,7 @@ case 'TypeSelector':

if (node.flags) {
pseudos['[' + node.flags.toLowerCase() + ']'] = true;
hasPseudo = true;
pseudos.add(`[${node.flags.toLowerCase()}]`);
}
break;
case 'WhiteSpace':
case 'Combinator':

@@ -95,3 +96,5 @@ tagName = '*';

// add property to all rule nodes to avoid multiple hidden class
node.pseudoSignature = hasPseudo && Object.keys(pseudos).sort().join(',');
node.pseudoSignature = pseudos.size > 0
? [...pseudos].sort().join(',')
: false;
};

@@ -1,13 +0,39 @@

module.exports = function specificity(simpleSelector) {
var A = 0;
var B = 0;
var C = 0;
import { parse } from 'css-tree';
simpleSelector.children.each(function walk(node) {
function ensureSelectorList(node) {
if (node.type === 'Raw') {
return parse(node.value, { context: 'selectorList' });
}
return node;
}
function maxSpecificity(a, b) {
for (let i = 0; i < 3; i++) {
if (a[i] !== b[i]) {
return a[i] > b[i] ? a : b;
}
}
return a;
}
function maxSelectorListSpecificity(selectorList) {
return ensureSelectorList(selectorList).children.reduce(
(result, node) => maxSpecificity(specificity(node), result),
[0, 0, 0]
);
}
// §16. Calculating a selector’s specificity
// https://www.w3.org/TR/selectors-4/#specificity-rules
function specificity(simpleSelector) {
let A = 0;
let B = 0;
let C = 0;
// A selector’s specificity is calculated for a given element as follows:
simpleSelector.children.forEach((node) => {
switch (node.type) {
case 'SelectorList':
case 'Selector':
node.children.each(walk);
break;
// count the number of ID selectors in the selector (= A)
case 'IdSelector':

@@ -17,2 +43,3 @@ A++;

// count the number of class selectors, attributes selectors, ...
case 'ClassSelector':

@@ -23,8 +50,54 @@ case 'AttributeSelector':

// ... and pseudo-classes in the selector (= B)
case 'PseudoClassSelector':
switch (node.name.toLowerCase()) {
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced
// by the specificity of the most specific complex selector in its selector list argument.
case 'not':
node.children.each(walk);
case 'has':
case 'is':
// :matches() is used before it was renamed to :is()
// https://github.com/w3c/csswg-drafts/issues/3258
case 'matches':
// Older browsers support :is() functionality as prefixed pseudo-class :any()
// https://developer.mozilla.org/en-US/docs/Web/CSS/:is
case '-webkit-any':
case '-moz-any': {
const [a, b, c] = maxSelectorListSpecificity(node.children.first);
A += a;
B += b;
C += c;
break;
}
// Analogously, the specificity of an :nth-child() or :nth-last-child() selector
// is the specificity of the pseudo class itself (counting as one pseudo-class selector)
// plus the specificity of the most specific complex selector in its selector list argument (if any).
case 'nth-child':
case 'nth-last-child': {
const arg = node.children.first;
if (arg.type === 'Nth' && arg.selector) {
const [a, b, c] = maxSelectorListSpecificity(arg.selector);
A += a;
B += b + 1;
C += c;
} else {
B++;
}
break;
}
// The specificity of a :where() pseudo-class is replaced by zero.
case 'where':
break;
// The four Level 2 pseudo-elements (::before, ::after, ::first-line, and ::first-letter) may,
// for legacy reasons, be represented using the <pseudo-class-selector> grammar,
// with only a single ":" character at their start.
// https://www.w3.org/TR/selectors-4/#single-colon-pseudos
case 'before':

@@ -37,3 +110,2 @@ case 'after':

// TODO: support for :nth-*(.. of <SelectorList>), :matches(), :has()
default:

@@ -44,9 +116,6 @@ B++;

case 'PseudoElementSelector':
C++;
break;
// count the number of type selectors ...
case 'TypeSelector':
// ignore universal selector
if (node.name.charAt(node.name.length - 1) !== '*') {
// ignore the universal selector
if (!node.name.endsWith('*')) {
C++;

@@ -56,2 +125,6 @@ }

// ... and pseudo-elements in the selector (= C)
case 'PseudoElementSelector':
C++;
break;
}

@@ -62,1 +135,3 @@ });

};
export default specificity;

@@ -1,6 +0,6 @@

var hasOwnProperty = Object.prototype.hasOwnProperty;
const { hasOwnProperty } = Object.prototype;
function isEqualSelectors(a, b) {
var cursor1 = a.head;
var cursor2 = b.head;
export function isEqualSelectors(a, b) {
let cursor1 = a.head;
let cursor2 = b.head;

@@ -15,5 +15,5 @@ while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {

function isEqualDeclarations(a, b) {
var cursor1 = a.head;
var cursor2 = b.head;
export function isEqualDeclarations(a, b) {
let cursor1 = a.head;
let cursor2 = b.head;

@@ -28,4 +28,4 @@ while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) {

function compareDeclarations(declarations1, declarations2) {
var result = {
export function compareDeclarations(declarations1, declarations2) {
const result = {
eq: [],

@@ -37,11 +37,11 @@ ne1: [],

var fingerprints = Object.create(null);
var declarations2hash = Object.create(null);
const fingerprints = Object.create(null);
const declarations2hash = Object.create(null);
for (var cursor = declarations2.head; cursor; cursor = cursor.next) {
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
declarations2hash[cursor.data.id] = true;
}
for (var cursor = declarations1.head; cursor; cursor = cursor.next) {
var data = cursor.data;
for (let cursor = declarations1.head; cursor; cursor = cursor.next) {
const data = cursor.data;

@@ -60,4 +60,4 @@ if (data.fingerprint) {

for (var cursor = declarations2.head; cursor; cursor = cursor.next) {
var data = cursor.data;
for (let cursor = declarations2.head; cursor; cursor = cursor.next) {
const data = cursor.data;

@@ -79,9 +79,9 @@ if (declarations2hash[data.id]) {

function addSelectors(dest, source) {
source.each(function(sourceData) {
var newStr = sourceData.id;
var cursor = dest.head;
export function addSelectors(dest, source) {
source.forEach((sourceData) => {
const newStr = sourceData.id;
let cursor = dest.head;
while (cursor) {
var nextStr = cursor.data.id;
const nextStr = cursor.data.id;

@@ -106,7 +106,7 @@ if (nextStr === newStr) {

// check if simpleselectors has no equal specificity and element selector
function hasSimilarSelectors(selectors1, selectors2) {
var cursor1 = selectors1.head;
export function hasSimilarSelectors(selectors1, selectors2) {
let cursor1 = selectors1.head;
while (cursor1 !== null) {
var cursor2 = selectors2.head;
let cursor2 = selectors2.head;

@@ -128,3 +128,3 @@ while (cursor2 !== null) {

// test node can't to be skipped
function unsafeToSkipNode(node) {
export function unsafeToSkipNode(node) {
switch (node.type) {

@@ -150,10 +150,1 @@ case 'Rule':

}
module.exports = {
isEqualSelectors: isEqualSelectors,
isEqualDeclarations: isEqualDeclarations,
compareDeclarations: compareDeclarations,
addSelectors: addSelectors,
hasSimilarSelectors: hasSimilarSelectors,
unsafeToSkipNode: unsafeToSkipNode
};

@@ -1,5 +0,5 @@

var hasOwnProperty = Object.prototype.hasOwnProperty;
const { hasOwnProperty } = Object.prototype;
function buildMap(list, caseInsensitive) {
var map = Object.create(null);
const map = Object.create(null);

@@ -10,5 +10,3 @@ if (!Array.isArray(list)) {

for (var i = 0; i < list.length; i++) {
var name = list[i];
for (let name of list) {
if (caseInsensitive) {

@@ -29,5 +27,5 @@ name = name.toLowerCase();

var tags = buildMap(data.tags, true);
var ids = buildMap(data.ids);
var classes = buildMap(data.classes);
const tags = buildMap(data.tags, true);
const ids = buildMap(data.ids);
const classes = buildMap(data.classes);

@@ -41,10 +39,10 @@ if (tags === null &&

return {
tags: tags,
ids: ids,
classes: classes
tags,
ids,
classes
};
}
function buildIndex(data) {
var scopes = false;
export function buildIndex(data) {
let scopes = false;

@@ -54,4 +52,4 @@ if (data.scopes && Array.isArray(data.scopes)) {

for (var i = 0; i < data.scopes.length; i++) {
var list = data.scopes[i];
for (let i = 0; i < data.scopes.length; i++) {
const list = data.scopes[i];

@@ -62,7 +60,5 @@ if (!list || !Array.isArray(list)) {

for (var j = 0; j < list.length; j++) {
var name = list[j];
for (const name of list) {
if (hasOwnProperty.call(scopes, name)) {
throw new Error('Class can\'t be used for several scopes: ' + name);
throw new Error(`Class can't be used for several scopes: ${name}`);
}

@@ -78,8 +74,4 @@

blacklist: buildList(data.blacklist),
scopes: scopes
scopes
};
}
module.exports = {
buildIndex: buildIndex
};
{
"name": "csso",
"version": "4.2.0",
"version": "5.0.0",
"description": "CSS minifier with structural optimisations",
"homepage": "https://github.com/css/csso",
"author": "Sergey Kryzhanovsky <skryzhanovsky@ya.ru> (https://github.com/afelix)",

@@ -14,7 +13,4 @@ "maintainers": [

],
"repository": "css/csso",
"license": "MIT",
"repository": "css/csso",
"bugs": {
"url": "https://github.com/css/csso/issues"
},
"keywords": [

@@ -29,39 +25,56 @@ "css",

],
"type": "module",
"main": "./lib/index",
"unpkg": "dist/csso.esm.js",
"jsdelivr": "dist/csso.esm.js",
"browser": {
"./cjs/version.cjs": "./dist/version.js",
"./lib/version.js": "./dist/version.js"
},
"exports": {
".": {
"import": "./lib/index.js",
"require": "./cjs/index.cjs"
},
"./dist/*": "./dist/*.js",
"./package.json": "./package.json"
},
"scripts": {
"test": "mocha --reporter dot",
"lint": "eslint lib test",
"test": "mocha test --reporter ${REPORTER:-progress}",
"test:cjs": "mocha cjs-test --reporter ${REPORTER:-progress}",
"test:dist": "mocha dist/test --reporter ${REPORTER:-progress}",
"lint": "eslint lib scripts test",
"lint-and-test": "npm run lint && npm test",
"build": "rollup --config && terser dist/csso.js --compress --mangle -o dist/csso.min.js",
"coverage": "nyc npm test",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"travis": "nyc npm run lint-and-test && npm run coveralls",
"hydrogen": "node --trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces --redirect-code-traces-to=code.asm --trace_hydrogen_file=code.cfg --print-opt-code bin/csso --stat -o /dev/null",
"prepublishOnly": "npm run build"
"build": "npm run bundle && npm run esm-to-cjs",
"build-and-test": "npm run build && npm run test:dist && npm run test:cjs",
"bundle": "node scripts/bundle",
"bundle-and-test": "npm run bundle && npm run test:dist",
"esm-to-cjs": "node scripts/esm-to-cjs",
"esm-to-cjs-and-test": "npm run esm-to-cjs && npm run test:cjs",
"coverage": "c8 --reporter=lcovonly npm test",
"prepublishOnly": "npm run lint-and-test && npm run build-and-test",
"hydrogen": "node --trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces --redirect-code-traces-to=code.asm --trace_hydrogen_file=code.cfg --print-opt-code bin/csso --stat -o /dev/null"
},
"dependencies": {
"css-tree": "^1.1.2"
"css-tree": "2.0.1"
},
"browser": {
"css-tree": "css-tree/dist/csstree.min.js"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.0.1",
"@rollup/plugin-json": "^4.0.1",
"@rollup/plugin-node-resolve": "^7.0.0",
"coveralls": "^3.0.11",
"eslint": "^6.8.0",
"mocha": "^7.1.1",
"nyc": "^15.0.0",
"rollup": "^1.29.0",
"source-map": "^0.6.1",
"terser": "^4.6.3"
"c8": "^7.10.0",
"coveralls": "^3.1.1",
"esbuild": "^0.14.1",
"eslint": "^7.24.0",
"mocha": "^9.1.2",
"rollup": "^2.60.2",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
"node": "^12.20.0 || ^14.13.0 || >=15.0.0",
"npm": ">=7.0.0"
},
"files": [
"dist",
"!dist/test",
"cjs",
"lib"
]
}
[![NPM version](https://img.shields.io/npm/v/csso.svg)](https://www.npmjs.com/package/csso)
[![Build Status](https://travis-ci.org/css/csso.svg?branch=master)](https://travis-ci.org/css/csso)
[![Build Status](https://github.com/css/csso/actions/workflows/build.yml/badge.svg)](https://github.com/css/csso/actions/workflows/build.yml)
[![Coverage Status](https://coveralls.io/repos/github/css/csso/badge.svg?branch=master)](https://coveralls.io/github/css/csso?branch=master)

@@ -7,9 +7,73 @@ [![NPM Downloads](https://img.shields.io/npm/dm/csso.svg)](https://www.npmjs.com/package/csso)

CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformations: cleaning (removing redundant), compression (replacement for shorter form) and restructuring (merge of declarations, rulesets and so on). As a result your CSS becomes much smaller.
CSSO (CSS Optimizer) is a CSS minifier. It performs three sort of transformations: cleaning (removing redundants), compression (replacement for the shorter forms) and restructuring (merge of declarations, rules and so on). As a result an output CSS becomes much smaller in size.
[![Originated by Yandex](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/yandex.svg)](https://www.yandex.com/)
[![Sponsored by Avito](https://cdn.rawgit.com/css/csso/8d1b89211ac425909f735e7d5df87ee16c2feec6/docs/avito.svg)](https://www.avito.ru/)
## Install
## Ready to use
```
npm install csso
```
## Usage
```js
import { minify } from 'csso';
// CommonJS is also supported
// const { minify } = require('csso');
const minifiedCss = minify('.test { color: #ff0000; }').css;
console.log(minifiedCss);
// .test{color:red}
```
Bundles are also available for use in a browser:
- `dist/csso.js` – minified IIFE with `csso` as global
```html
<script src="node_modules/csso/dist/csso.js"></script>
<script>
csso.minify('.example { color: green }');
</script>
```
- `dist/csso.esm.js` – minified ES module
```html
<script type="module">
import { minify } from 'node_modules/csso/dist/csso.esm.js'
minify('.example { color: green }');
</script>
```
One of CDN services like `unpkg` or `jsDelivr` can be used. By default (for short path) a ESM version is exposing. For IIFE version a full path to a bundle should be specified:
```html
<!-- ESM -->
<script type="module">
import * as csstree from 'https://cdn.jsdelivr.net/npm/csso';
import * as csstree from 'https://unpkg.com/csso';
</script>
<!-- IIFE with an export to global -->
<script src="https://cdn.jsdelivr.net/npm/csso/dist/csso.js"></script>
<script src="https://unpkg.com/csso/dist/csso.js"></script>
```
CSSO is based on [CSSTree](https://github.com/csstree/csstree) to parse CSS into AST, AST traversal and to generate AST back to CSS. All `CSSTree` API is available behind `syntax` field extended with `compress()` method. You may minify CSS step by step:
```js
import { syntax } from 'csso';
const ast = syntax.parse('.test { color: #ff0000; }');
const compressedAst = syntax.compress(ast).ast;
const minifiedCss = syntax.generate(compressedAst);
console.log(minifiedCss);
// .test{color:red}
```
> Warning: CSSO doesn't guarantee API behind a `syntax` field as well as AST format. Both might be changed with changes in CSSTree. If you rely heavily on `syntax` API, a better option might be to use CSSTree directly.
## Related projects
- [Web interface](http://css.github.io/csso/csso.html)

@@ -24,9 +88,6 @@ - [csso-cli](https://github.com/css/csso-cli) – command line interface

- [CSSO Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=Aneryu.csso)
- [vscode-csso](https://github.com/1000ch/vscode-csso) - Visual Studio Code plugin
- [atom-csso](https://github.com/1000ch/atom-csso) - Atom plugin
- [Sublime-csso](https://github.com/1000ch/Sublime-csso) - Sublime plugin
## Install
```
npm install csso
```
## API

@@ -47,27 +108,2 @@

Basic usage:
```js
var csso = require('csso');
var minifiedCss = csso.minify('.test { color: #ff0000; }').css;
console.log(minifiedCss);
// .test{color:red}
```
CSSO is based on [CSSTree](https://github.com/csstree/csstree) to parse CSS into AST, AST traversal and to generate AST back to CSS. All `CSSTree` API is available behind `syntax` field. You may minify CSS step by step:
```js
var csso = require('csso');
var ast = csso.syntax.parse('.test { color: #ff0000; }');
var compressedAst = csso.syntax.compress(ast).ast;
var minifiedCss = csso.syntax.generate(compressedAst);
console.log(minifiedCss);
// .test{color:red}
```
> Warning: CSSO uses early versions of CSSTree that still in active development. CSSO doesn't guarantee API behind `syntax` field or AST format will not change in future releases of CSSO, since it's subject to change in CSSTree. Be careful with CSSO updates if you use `syntax` API until this warning removal.
### minify(source[, options])

@@ -78,3 +114,3 @@

```js
var result = csso.minify('.test { color: #ff0000; }', {
const result = csso.minify('.test { color: #ff0000; }', {
restructure: false, // don't change CSS structure, i.e. don't merge declarations, rulesets etc

@@ -138,3 +174,3 @@ debug: true // show additional debug information:

```js
var result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000');
const result = csso.minifyBlock('color: rgba(255, 0, 0, 1); color: #ff0000');

@@ -208,5 +244,5 @@ console.log(result.css);

```js
var csso = require('csso');
var css = fs.readFileSync('path/to/my.css', 'utf8');
var result = csso.minify(css, {
const csso = require('csso');
const css = fs.readFileSync('path/to/my.css', 'utf8');
const result = csso.minify(css, {
filename: 'path/to/my.css', // will be added to source map as reference to source file

@@ -226,8 +262,9 @@ sourceMap: true // generate source map

```js
var require('source-map');
var csso = require('csso');
var inputFile = 'path/to/my.css';
var input = fs.readFileSync(inputFile, 'utf8');
var inputMap = input.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
var output = csso.minify(input, {
import { SourceMapConsumer } from 'source-map';
import * as csso from 'csso';
const inputFile = 'path/to/my.css';
const input = fs.readFileSync(inputFile, 'utf8');
const inputMap = input.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
const output = csso.minify(input, {
filename: inputFile,

@@ -249,3 +286,3 @@ sourceMap: true

'/*# sourceMappingURL=data:application/json;base64,' +
new Buffer(output.map.toString()).toString('base64') +
Buffer.from(output.map.toString()).toString('base64') +
' */'

@@ -252,0 +289,0 @@ );

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc