2 * Sqwish - a CSS Compressor
3 * Copyright Dustin Diaz 2011
4 * https://github.com/ded/sqwish
8 var fs = require('fs');
13 for (i = ar.length - 1; i >= 0; i--) {
14 for (j = a.length - 1; j >= 0; j--) {
24 function sqwish(css, strict) {
25 // allow /*! bla */ style comments to retain copyrights etc.
26 var comments = css.match(/\/\*![\s\S]+?\*\//g);
28 css = css.trim() // give it a solid trimming to start
31 .replace(/\/\*[\s\S]+?\*\//g, '')
33 // line breaks and carriage returns
34 .replace(/[\n\r]/g, '')
36 // space between selectors, declarations, properties and values
37 .replace(/\s*([:;,{}])\s*/g, '$1')
39 // replace multiple spaces with single spaces
42 // space between last declaration and end of rule
43 // also remove trailing semi-colons on last declaration
47 .replace(/\s+(!important)/g, '$1')
49 // convert longhand hex to shorthand hex
50 .replace(/#([a-fA-F0-9])\1([a-fA-F0-9])\2([a-fA-F0-9])\3/g, '#$1$2$3')
52 // replace longhand values with shorthand '5px 5px 5px 5px' => '5px'
53 .replace(/(\d+[a-z]{2}) \1 \1 \1/gi, '$1')
55 // replace double-specified longhand values with shorthand '5px 2px 5px 2px' => '5px 2px'
56 .replace(/(\d+[a-z]{2}) (\d+[a-z]{2}) \1 \2/gi, '$1 $2')
59 .replace(/([\s|:])[0]+px/g, '$10');
62 css = strict_css(css);
65 // put back in copyrights
67 comments = comments ? comments.join('\n') : '';
68 css = comments + '\n' + css;
73 function strict_css(css) {
74 // now some super fun funky shit where we remove duplicate declarations
75 // into combined rules
77 // store global dict of all rules
79 rules = css.match(/([^{]+\{[^}]+\})+?/g);
82 rules.forEach(function (rule) {
83 // break rule into selector|declaration parts
84 var parts = rule.match(/([^{]+)\{([^}]+)/),
86 declarations = parts[2];
88 // start new list if it wasn't created already
89 if (!ruleList[selector]) {
90 ruleList[selector] = [];
93 declarations = declarations.split(';');
94 // filter out duplicate properties
95 ruleList[selector] = ruleList[selector].filter(function (decl) {
96 var prop = decl.match(/[^:]+/)[0];
97 // pre-existing properties are not wanted anymore
98 return !declarations.some(function (dec) {
99 // must include '^' as to not confuse "color" with "border-color" etc.
100 return dec.match(new RegExp('^' + prop + ':'));
104 // latter takes presedence :)
105 ruleList[selector] = ruleList[selector].concat(declarations);
106 // still dups? just in case
107 ruleList[selector] = uniq(ruleList[selector]);
110 // reset css because we're gonna recreate the whole shabang.
112 for (var selector in ruleList) {
113 var joinedRuleList = ruleList[selector].join(';');
114 css += selector + '{' + (joinedRuleList).replace(/;$/, '') + '}';
119 module.exports.exec = function (args) {
122 if (out = args.indexOf('-o') != -1) {
125 out = read.replace(/\.css$/, '.min.css');
127 console.log('compressing ' + read + ' to ' + out + '...');
128 var data = fs.readFileSync(read, 'utf8');
129 fs.writeFileSync(out, sqwish(data, (args.indexOf('--strict') != -1)), 'utf8');
131 module.exports.minify = function (css, strict) {
132 return sqwish(css, strict);