Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

clean-css

Package Overview
Dependencies
Maintainers
1
Versions
211
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

clean-css - npm Package Compare versions

Comparing version 0.3.3 to 0.4.0

test/data/big-min.css

8

History.md

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

0.4.0 / 2012-06-04
==================
* Speed improvements (up to 4x) thanks to rewrite of comments and CSS' content processing.
* Stripping empty CSS tags is now optional (see ./bin/cleancss for details).
* Improved debugging mode (see ./test/bench.js)
* Added `make bench` for a one-pass benchmark.
0.3.3 / 2012-05-27

@@ -2,0 +10,0 @@ ==================

165

lib/clean.js

@@ -11,5 +11,13 @@ var util = require('util');

specialComments: [],
contentBlocks: [],
process: function(data, options) {
var specialComments = [],
contentBlocks = [];
var self = this,
replace = function(pattern, replacement) {
if (typeof arguments[0] == 'function')
arguments[0]();
else
data = data.replace.apply(data, arguments);
};

@@ -19,48 +27,24 @@ options = options || {};

// replace function
var replace = function(pattern, replacement) {
if (options.debug) { // for debugging purposes only
console.time(pattern);
data = data.replace(pattern, replacement);
console.timeEnd(pattern);
var end = new Date().getTime();
} else {
data = data.replace(pattern, replacement);
}
};
if (options.debug) {
var originalReplace = replace;
replace = function(pattern, replacement) {
var name = typeof pattern == 'function' ?
/function (\w+)\(/.exec(pattern.toString())[1] :
pattern;
console.time(name);
originalReplace(pattern, replacement);
console.timeEnd(name);
};
}
// strip comments one by one
for (var end = 0; end < data.length; ) {
var start = data.indexOf('/*', end);
end = data.indexOf('*/', start);
if (start == -1 || end == -1) break;
replace(function stripComments() {
data = self.stripComments(data);
});
if (data[start + 2] == '!') {
// in case of special comments, replace them with a placeholder
specialComments.push(data.substring(start, end + 2));
data = data.substring(0, start) + '__CSSCOMMENT__' + data.substring(end + 2);
} else {
data = data.substring(0, start) + data.substring(end + 2);
}
end = start;
}
// replace content: with a placeholder
for (var end = 0; end < data.length; ) {
var start = data.indexOf('content', end);
if (start == -1) break;
replace(function stripContent() {
data = self.stripContent(data);
});
var wrapper = /[^ :]/.exec(data.substring(start + 7))[0];
if (/['"]/.test(wrapper) == false) {
end = start + 7;
continue;
}
var firstIndex = data.indexOf(wrapper, start);
var lastIndex = data.indexOf(wrapper, firstIndex + 1);
contentBlocks.push(data.substring(firstIndex, lastIndex + 1));
data = data.substring(0, firstIndex) + '__CSSCONTENT__' + data.substring(lastIndex + 1);
end = lastIndex + 1;
}
replace(/;\s*;+/g, ';') // whitespace between semicolons & multiple semicolons

@@ -112,3 +96,3 @@ replace(/\n/g, '') // line breaks

replace(/([: ,=\-])0\.(\d)/g, '$1.$2')
replace(/[^}]+?{\s*?}/g, '') // empty elements
if (options.removeEmpty) replace(/[^}]+?{\s*?}/g, '') // empty elements
if (data.indexOf('charset') > 0) replace(/(.+)(@charset [^;]+;)/, '$2$1') // move first charset to the beginning

@@ -124,6 +108,95 @@ replace(/(.)(@charset [^;]+;)/g, '$1') // remove all extra charsets that are not at the beginning

});
replace(/__CSSCOMMENT__/g, function() { return specialComments.shift(); });
replace(/__CSSCONTENT__/g, function() { return contentBlocks.shift(); });
replace(/__CSSCOMMENT__/g, function() { return self.specialComments.shift(); });
replace(/__CSSCONTENT__/g, function() { return self.contentBlocks.shift(); });
return data.trim() // trim spaces at beginning and end
},
// Strips special comments (/*! ... */) by replacing them by __CSSCOMMENT__ marker
// for further restoring. Plain comments are removed. It's done by scanning datq using
// String#indexOf scanning instead of regexps to speed up the process.
stripComments: function(data) {
var tempData = [],
nextStart = 0,
nextEnd = 0,
cursor = 0;
for (; nextEnd < data.length; ) {
nextStart = data.indexOf('/*', nextEnd);
nextEnd = data.indexOf('*/', nextStart);
if (nextStart == -1 || nextEnd == -1) break;
tempData.push(data.substring(cursor, nextStart))
if (data[nextStart + 2] == '!') {
// in case of special comments, replace them with a placeholder
this.specialComments.push(data.substring(nextStart, nextEnd + 2));
tempData.push('__CSSCOMMENT__');
}
cursor = nextEnd + 2;
}
return tempData.length > 0 ?
tempData.join('') + data.substring(cursor, data.length) :
data;
},
// Strips content tags by replacing them by __CSSCONTENT__ marker
// for further restoring. It's done via string scanning instead of
// regexps to speed up the process.
stripContent: function(data) {
var tempData = [],
nextStart = 0,
nextEnd = 0,
tempStart = 0,
cursor = 0,
matchedParenthesis = null;
// Finds either first (matchedParenthesis == null) or second matching parenthesis
// so we can determine boundaries of content block.
var nextParenthesis = function(pos) {
var min,
max = data.length;
if (matchedParenthesis) {
min = data.indexOf(matchedParenthesis, pos);
if (min == -1) min = max;
} else {
var next1 = data.indexOf("'", pos);
var next2 = data.indexOf('"', pos);
if (next1 == -1) next1 = max;
if (next2 == -1) next2 = max;
min = next1 > next2 ? next2 : next1;
}
if (min == max) return -1;
if (matchedParenthesis) {
matchedParenthesis = null;
return min;
} else {
// check if there's anything else between pos and min that doesn't match ':' or whitespace
if (/[^:\s]/.test(data.substring(pos, min))) return -1;
matchedParenthesis = data.charAt(min);
return min + 1;
}
};
for (; nextEnd < data.length; ) {
nextStart = data.indexOf('content', nextEnd);
if (nextStart == -1) break;
nextStart = nextParenthesis(nextStart + 7);
nextEnd = nextParenthesis(nextStart);
if (nextStart == -1 || nextEnd == -1) break;
tempData.push(data.substring(cursor, nextStart - 1));
tempData.push('__CSSCONTENT__');
this.contentBlocks.push(data.substring(nextStart - 1, nextEnd + 1));
cursor = nextEnd + 1;
}
return tempData.length > 0 ?
tempData.join('') + data.substring(cursor, data.length) :
data;
}

@@ -130,0 +203,0 @@ };

@@ -11,3 +11,3 @@ {

},
"version": "0.3.3",
"version": "0.4.0",
"main": "index.js",

@@ -14,0 +14,0 @@ "bin": {

@@ -20,3 +20,3 @@ ## What is clean-css? ##

cleancss -o public-min.css public.css
To minify the same **public.css** into standard output skip the -o parameter:

@@ -29,3 +29,3 @@

cat one.css two.css three.css | cleancss -o merged-and-minified.css
Or even gzip it at once:

@@ -38,3 +38,3 @@

var cleanCSS = require('clean-css');
var source = "a{font-weight:bold;}";

@@ -49,4 +49,9 @@ var minimized = cleanCSS.process(source);

### Acknowledgments ###
* Vincent Voyer (@vvo) for a patch with better empty element regex and for inspiring us to do many performance improvements in 0.4 release.
* Jan Michael Alonzo (@jmalonzo) for a patch removing node's old 'sys' package.
## License ##
Clean-css is released under the MIT license.

@@ -22,3 +22,3 @@ var vows = require('vows'),

context[testName]['minimizing ' + testName + '.css'] = function(data) {
assert.equal(cleanCSS.process(data.plain), data.minimized)
assert.equal(cleanCSS.process(data.plain, { removeEmpty: true }), data.minimized)
};

@@ -25,0 +25,0 @@ });

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

var
path = require('path'),
cleanCSS = require('../index'),
bigcss = require('fs').readFileSync(path.join(__dirname, 'data', 'big.css'), 'utf8'),
runs = ~~process.argv[2] || 10;
var cleanCSS = require('../index'),
bigcss = require('fs').readFileSync(require('path').join(__dirname, 'data', 'big.css'), 'utf8');
for(var i=0; i<runs; i++) {
console.time('complete minification');
cleanCSS.process(bigcss, { debug: true });
console.timeEnd('complete minification');
}
console.time('complete minification');
cleanCSS.process(bigcss, { debug: true });
console.timeEnd('complete minification');

@@ -5,10 +5,10 @@ var vows = require('vows'),

var cssContext = function(groups) {
var cssContext = function(groups, options) {
var context = {};
var clean = function(cleanedCSS) {
return function(css) {
assert.equal(cleanCSS.process(css), cleanedCSS);
assert.equal(cleanCSS.process(css, options), cleanedCSS);
}
};
for (var g in groups) {

@@ -20,3 +20,3 @@ var transformation = groups[g];

}
for (var i = 0, c = transformation.length; i < c; i++) {

@@ -29,3 +29,3 @@ context[g + ' #' + (i + 1)] = {

}
return context;

@@ -102,16 +102,2 @@ };

}),
'empty elements': cssContext({
'single': [
' div p { \n}',
''
],
'between non-empty': [
'div {color:#fff} a{ } p{ line-height:1.35em}',
'div{color:#fff}p{line-height:1.35em}'
],
'just a semicolon': [
'div { ; }',
''
]
}),
'selectors': cssContext({

@@ -171,3 +157,7 @@ 'remove spaces around selectors': [

'text content': cssContext({
'normal': 'a{content:"."}',
'normal #1': 'a{content:"."}',
'normal #2': [
'a:before{content : "test\'s test"; }',
'a:before{content:"test\'s test"}'
],
'open quote': [

@@ -311,3 +301,23 @@ 'a{content : open-quote;opacity:1}',

]
}),
'empty elements': cssContext({
'single': [
' div p { \n}',
''
],
'between non-empty': [
'div {color:#fff} a{ } p{ line-height:1.35em}',
'div{color:#fff}p{line-height:1.35em}'
],
'just a semicolon': [
'div { ; }',
''
]
}, { removeEmpty: true }),
'skip empty elements': cssContext({
'empty #1': 'a{}',
'empty #2': 'div>a{}',
'empty #3': 'div:nth-child(2n){}',
'empty #3': 'a{color:#fff}div{}p{line-height:2em}'
})
}).export(module);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc