Comparing version 0.7.3 to 0.7.4
@@ -7,3 +7,2 @@ 'use strict'; | ||
request = require('request'), | ||
spawn = require('child_process').spawn, | ||
phantom = require('phantomjs'), | ||
@@ -23,36 +22,25 @@ _ = require('underscore'); | ||
filename, | ||
timeout || 5000 | ||
], | ||
page, | ||
buffer = '', | ||
error = ''; | ||
timeout | ||
]; | ||
page = spawn(phantom.path, childArgs); | ||
page.stdout.setEncoding('utf8'); | ||
page.stderr.setEncoding('utf8'); | ||
require('child_process').execFile(phantom.path, childArgs, function (err, stdout, stderr) { | ||
/* Filter OSX-specific error */ | ||
var isNot192OSXError = (err || stderr || '').indexOf('WARNING: Method userSpaceScaleFactor') === -1, | ||
isNotPerformanceNote = (err || stderr || '').indexOf('CoreText performance note:') === -1; | ||
page.stdout.on('data', function (data) { | ||
buffer += data; | ||
}); | ||
page.stderr.on('data', function (data) { | ||
// Ignore PhantomJS 1.9.2 OSX errors | ||
var bufferStr = data + '', | ||
isNot192OSXError = bufferStr.indexOf('WARNING: Method userSpaceScaleFactor') === -1, | ||
isNotPerformanceNote = bufferStr.indexOf('CoreText performance note:') === -1; | ||
if (isNot192OSXError && isNotPerformanceNote) { | ||
error += bufferStr; | ||
/* istanbul ignore if: not testable */ | ||
if (err) { | ||
if (isNot192OSXError && isNotPerformanceNote) { | ||
return callback(err); | ||
} | ||
} | ||
}); | ||
page.on('close', function (code) { | ||
if (code === 0 && error === '') { | ||
callback(buffer); | ||
} else { | ||
// If we have a warning from Phantom, we just report it | ||
// (no need to exit the process) | ||
console.error('PhantomJS: ', error); | ||
callback(buffer); | ||
/* istanbul ignore if: not testable */ | ||
if (stderr) { | ||
/* Our error */ | ||
if (isNot192OSXError && isNotPerformanceNote) { | ||
return callback(stderr); | ||
} | ||
} | ||
/* Success */ | ||
return callback(null, stdout); | ||
}); | ||
@@ -68,3 +56,3 @@ } | ||
function mapReadFiles(files, callback) { | ||
async.map(files, function (filename, done) { | ||
return async.map(files, function (filename, done) { | ||
if (filename.match(/^http/)) { | ||
@@ -85,3 +73,3 @@ request( | ||
} | ||
return done('mapReadFiles Error: could not find: ' + path.join(process.cwd(), filename)); | ||
return done('UnCSS: could not open ' + path.join(process.cwd(), filename)); | ||
} | ||
@@ -97,3 +85,3 @@ }, callback); | ||
*/ | ||
function extract_stylesheets(dom, options) { | ||
function extractStylesheets(dom, options) { | ||
var media = _.union(['screen', 'all'], options.media), | ||
@@ -165,3 +153,9 @@ stylesheets; | ||
}).join(' '); | ||
match = doms[i](temp); | ||
/* istanbul ignore next: need examples */ | ||
try { | ||
match = doms[i](temp); | ||
} catch(e) { | ||
/* Gracefully give up */ | ||
return true; | ||
} | ||
} | ||
@@ -222,3 +216,3 @@ if (match.length !== 0) { | ||
module.exports.mapReadFiles = mapReadFiles; | ||
module.exports.extract_stylesheets = extract_stylesheets; | ||
module.exports.extractStylesheets = extractStylesheets; | ||
module.exports.filterUnusedRules = filterUnusedRules; |
@@ -11,3 +11,3 @@ /* global phantom */ | ||
}); | ||
console.log(p); | ||
system.stdout.writeLine(p); | ||
phantom.exit(); | ||
@@ -17,7 +17,7 @@ } | ||
if (system.args.length < 2) { | ||
system.stdout.writeLine('PhantomJS: no filename provided.'); | ||
system.stderr.writeLine('PhantomJS: no filename provided.'); | ||
phantom.exit(1); | ||
} | ||
if (system.args.length < 3) { | ||
system.stdout.writeLine('PhantomJS: no timeout provided.'); | ||
system.stderr.writeLine('PhantomJS: no timeout provided.'); | ||
phantom.exit(1); | ||
@@ -29,3 +29,3 @@ } | ||
if (status !== 'success') { | ||
console.log('PhantomJS: Unable to open: ' + system.args[1]); | ||
system.stderr.writeLine('PhantomJS: Unable to open: ' + system.args[1]); | ||
phantom.exit(); | ||
@@ -32,0 +32,0 @@ } else { |
151
lib/uncss.js
@@ -6,5 +6,5 @@ 'use strict'; | ||
cheerio = require('cheerio'), | ||
lib = require('./lib.js'), | ||
path = require('path'), | ||
url = require('url'), | ||
utility = require('./lib.js'), | ||
_ = require('underscore'); | ||
@@ -35,57 +35,57 @@ | ||
stylesheets = doms.map(function (html) { | ||
return utility.extract_stylesheets(html, options); | ||
return lib.extractStylesheets(html, options); | ||
}); | ||
} | ||
if (_.flatten(stylesheets).length !== 0) { | ||
/* Only run this if we found links to stylesheets (there may be none...) | ||
* files = ['some_file.html', 'some_other_file.html'] | ||
* stylesheets = [['relative_css_path.css', ...], | ||
* ['maybe_a_duplicate.css', ...]] | ||
* We need to - make the stylesheets' paths relative to the HTML files, | ||
* - flatten the array, | ||
* - remove duplicates | ||
*/ | ||
stylesheets = stylesheets.map(function (arr, i) { | ||
return arr.map(function (el) { | ||
var u, p; | ||
if (files[i].match(/^http/)) { | ||
u = url.parse(files[i]); | ||
p = u.protocol + '//' + u.host; | ||
/* If the href to the stylesheet is an absolute path, we | ||
* use it directly. | ||
* If it starts with a / we use the host as the base url | ||
* otherwise we use the current path of the url as the | ||
* base url | ||
*/ | ||
if (el.substr(0, 4) === 'http') { | ||
p = el; | ||
} else if (el.substr(0, 2) === '//') { | ||
p = 'http:' + el; | ||
} else if (el[0] === '/') { | ||
p += el; | ||
} else { | ||
p += path.join(u.pathname, el); | ||
} | ||
if (_.flatten(stylesheets).length !== 0) { | ||
/* Only run this if we found links to stylesheets (there may be none...) | ||
* files = ['some_file.html', 'some_other_file.html'] | ||
* stylesheets = [['relative_css_path.css', ...], | ||
* ['maybe_a_duplicate.css', ...]] | ||
* We need to - make the stylesheets' paths relative to the HTML files, | ||
* - flatten the array, | ||
* - remove duplicates | ||
*/ | ||
stylesheets = stylesheets.map(function (arr, i) { | ||
return arr.map(function (el) { | ||
var u, p; | ||
if (files[i].match(/^http/)) { | ||
u = url.parse(files[i]); | ||
p = u.protocol + '//' + u.host; | ||
/* If the href to the stylesheet is an absolute path, we | ||
* use it directly. | ||
* If it starts with a / we use the host as the base url | ||
* otherwise we use the current path of the url as the | ||
* base url | ||
*/ | ||
if (el.substr(0, 4) === 'http') { | ||
p = el; | ||
} else if (el.substr(0, 2) === '//') { | ||
p = 'http:' + el; | ||
} else if (el[0] === '/') { | ||
p += el; | ||
} else { | ||
if (el.substr(0, 4) !== 'http') { | ||
p = path.join(path.dirname(files[i]), options.csspath, el); | ||
} else { | ||
p = el; | ||
} | ||
p += path.join(u.pathname.substring(0, u.pathname.lastIndexOf("/")), el); | ||
} | ||
return p; | ||
}); | ||
} else { | ||
if (el.substr(0, 4) !== 'http') { | ||
p = path.join(path.dirname(files[i]), options.csspath, el); | ||
} else { | ||
p = el; | ||
} | ||
} | ||
return p; | ||
}); | ||
stylesheets = _.flatten(stylesheets); | ||
stylesheets = stylesheets.filter(function (e, i, arr) { | ||
return arr.lastIndexOf(e) === i; | ||
}); | ||
} else { | ||
/* Reset the array if we didn't find any link tags */ | ||
stylesheets = []; | ||
} | ||
}); | ||
stylesheets = _.flatten(stylesheets); | ||
stylesheets = stylesheets.filter(function (e, i, arr) { | ||
return arr.lastIndexOf(e) === i; | ||
}); | ||
} else { | ||
/* Reset the array if we didn't find any link tags */ | ||
stylesheets = []; | ||
} | ||
stylesheets = utility.mapReadFiles(stylesheets, function (err, stylesheets) { | ||
stylesheets = lib.mapReadFiles(stylesheets, function (err, stylesheets) { | ||
if (err) { | ||
throw err; | ||
return callback(err); | ||
} | ||
@@ -97,3 +97,3 @@ /* If we specified a raw string of CSS, add it to the stylesheets array */ | ||
} else { | ||
throw 'TypeError: options.raw - expected a string'; | ||
return callback('UnCSS: options.raw - expected a string'); | ||
} | ||
@@ -109,4 +109,3 @@ } | ||
if (_.flatten(stylesheets).length === 0) { | ||
callback(''); | ||
return; | ||
return callback('UnCSS: no stylesheets found'); | ||
} | ||
@@ -120,7 +119,14 @@ | ||
*/ | ||
var css_str = stylesheets.join(' \n'); | ||
var parsed = css.parse(css_str); | ||
var used_css = utility.filterUnusedRules(doms, parsed.stylesheet, options.ignore); | ||
var css_str = stylesheets.join(' \n'), | ||
parsed, used_css; | ||
callback(css.stringify(used_css) + '\n'); | ||
try { | ||
parsed = css.parse(css_str); | ||
} catch (err) { | ||
err.message = 'node_modules/css: ' + err.message; | ||
return callback(err); | ||
} | ||
used_css = lib.filterUnusedRules(doms, parsed.stylesheet, options.ignore); | ||
return callback(null, css.stringify(used_css) + '\n'); | ||
}); | ||
@@ -150,3 +156,3 @@ | ||
} else { | ||
throw 'TypeError: expected a callback'; | ||
throw new TypeError('UnCSS: expected a callback'); | ||
} | ||
@@ -158,17 +164,4 @@ | ||
options.media = options.media || []; | ||
options.timeout = options.timeout || 0; // By default, it exits when all js is executed | ||
var process = function (doms) { | ||
async.map( | ||
doms, | ||
function (f, oncomplete) { | ||
return utility.phantom_eval(f, options.timeout, oncomplete); | ||
}, | ||
function (res) { | ||
if (typeof res !== 'Array') { | ||
res = [res]; | ||
} | ||
uncss(files, res, options, callback); | ||
} | ||
); | ||
}; | ||
/* If 'files' is a string, it should represent an HTML page. */ | ||
@@ -178,3 +171,3 @@ if (typeof files === 'string') { | ||
files = ['']; | ||
process(doms); | ||
return uncss(files, doms, options, callback); | ||
} else { | ||
@@ -184,3 +177,14 @@ if (opt.urls && opt.urls.length > 0) { | ||
} | ||
process(files); | ||
return async.map( | ||
files, | ||
function (f, oncomplete) { | ||
return lib.phantom_eval(f, options.timeout, oncomplete); | ||
}, | ||
function (err, res) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
uncss(files, res, options, callback); | ||
} | ||
); | ||
} | ||
@@ -190,3 +194,2 @@ | ||
// TODO: This might be counterintuitive | ||
module.exports = init; |
{ | ||
"name": "uncss", | ||
"version": "0.7.3", | ||
"version": "0.7.4", | ||
"description": "Remove unused CSS styles", | ||
@@ -10,3 +10,3 @@ "main": "lib/uncss.js", | ||
"scripts": { | ||
"test": "mocha tests/*.js --reporter spec" | ||
"test": "make test" | ||
}, | ||
@@ -41,4 +41,6 @@ "bin": { | ||
"mocha": "~1.17.0", | ||
"chai": "~1.8.1" | ||
"chai": "~1.8.1", | ||
"coveralls": "~2.6.1", | ||
"mocha-lcov-reporter": "0.0.1" | ||
} | ||
} |
# UnCSS # | ||
[![Build Status](https://travis-ci.org/giakki/uncss.png)](https://travis-ci.org/giakki/uncss) | ||
[![Coverage Status](https://coveralls.io/repos/giakki/uncss/badge.png?branch=master)](https://coveralls.io/r/giakki/uncss?branch=master) | ||
[![Dependency Status](https://gemnasium.com/giakki/uncss.png)](https://gemnasium.com/giakki/uncss) | ||
@@ -30,3 +32,3 @@ UnCSS is a tool that removes unused CSS from your stylesheets. | ||
uncss(files, options, function (output) { | ||
uncss(files, options, function (error, output) { | ||
console.log(output); | ||
@@ -36,9 +38,11 @@ }); | ||
/* Look Ma, no options! */ | ||
uncss(files, function (output) { | ||
uncss(files, function (error, output) { | ||
console.log(output); | ||
}); | ||
/* Specifying raw HTML*/ | ||
/* Specifying raw HTML | ||
* NOTE: raw HTML is not parsed by phantom | ||
*/ | ||
var raw_html = '...' | ||
uncss(raw_html, options, function (output) { | ||
uncss(raw_html, options, function (error, output) { | ||
console.log(output); | ||
@@ -45,0 +49,0 @@ }); |
@@ -0,1 +1,2 @@ | ||
/* jshint expr: true */ | ||
'use strict'; | ||
@@ -13,6 +14,7 @@ | ||
var stylesheets = ['coverage/override.css'], | ||
var stylesheets = ['coverage/override.css', 'coverage/ignore.css'], | ||
rawcss = rfs('coverage/raw.css'), | ||
options = { | ||
csspath: 'tests', | ||
ignore: ['.unused_test', /^#test/], | ||
stylesheets: stylesheets, | ||
@@ -22,41 +24,36 @@ raw: rawcss | ||
describe('UnCSS', function () { | ||
describe('Options', function () { | ||
describe('Basic functionality', function () { | ||
var output = false; | ||
var output; | ||
before(function (done) { | ||
uncss('<html><body></body></html>', function (res) { | ||
output = res; | ||
done(); | ||
}); | ||
before(function (done) { | ||
uncss(rfs('selectors/index.html'), options, function (err, res) { | ||
if (err) { | ||
throw err; | ||
} | ||
output = res; | ||
done(); | ||
}); | ||
}); | ||
it('should output something', function () { | ||
expect(output).not.to.equal(false); | ||
}); | ||
it('options.stylesheets should override <link> tags', function () { | ||
expect(output).to.include(rfs(stylesheets[0])); | ||
}); | ||
it('should be an empty string', function () { | ||
expect(output).to.equal(''); | ||
}); | ||
it('options.raw should be added to the processed CSS', function () { | ||
expect(output).to.include(rawcss); | ||
}); | ||
describe('Options', function () { | ||
var output; | ||
it('options.ignore should be added to the output and accept a regex', function () { | ||
expect(output).to.include(rfs(stylesheets[1])); | ||
}); | ||
before(function (done) { | ||
uncss(rfs('index.html'), options, function (res) { | ||
output = res; | ||
done(); | ||
}); | ||
it('options.urls should be processed', function (done) { | ||
this.timeout(25000); | ||
uncss([], { urls: ['http://giakki.github.io/uncss/'] }, function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.exist; | ||
done(); | ||
}); | ||
it('options.stylesheets should override <link> tags', function () { | ||
expect(output).to.include(rfs(stylesheets[0])); | ||
}); | ||
it('options.raw should be added to the processed CSS', function () { | ||
expect(output).to.include(rawcss); | ||
}); | ||
}); | ||
}); |
@@ -0,1 +1,4 @@ | ||
/* jshint expr: true */ | ||
'use strict'; | ||
var expect = require('chai').expect, | ||
@@ -6,6 +9,8 @@ fs = require('fs'), | ||
describe("Compile the CSS of an html page passed by path", function () { | ||
'use strict'; | ||
it("Should compile two stylesheets into one and keep the media query", function (done) { | ||
this.timeout(10000); | ||
uncss(["tests/input/testpage.html"], function (output) { | ||
this.timeout(25000); | ||
uncss(["tests/input/testpage.html"], function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.exist; | ||
@@ -12,0 +17,0 @@ fs.writeFile(__dirname+"/output/testpage.compiled.css", output, done); |
@@ -9,17 +9,31 @@ 'use strict'; | ||
/* Read file sync sugar. */ | ||
var rfs = function (file) { | ||
return fs.readFileSync(path.join(__dirname, file), 'utf-8').toString(); | ||
}; | ||
function rfs(file) { | ||
var filename = path.join(__dirname, file); | ||
if (fs.existsSync(filename)) { | ||
return fs.readFileSync(filename, 'utf-8'); | ||
} | ||
return null; | ||
} | ||
var rawcss = false; | ||
var tests = fs.readdirSync(path.join(__dirname, 'fixtures/')); | ||
var input = ''; | ||
var rawcss = false, | ||
fixtures = fs.readdirSync(path.join(__dirname, 'selectors/fixtures')), | ||
expected = fs.readdirSync(path.join(__dirname, 'selectors/expected')), | ||
unused = fs.readdirSync(path.join(__dirname, 'selectors/unused')), | ||
tests; | ||
/* Only read through CSS files */ | ||
tests.forEach(function (test, i) { | ||
if (test.indexOf('.css') > -1) { | ||
input += rfs('fixtures/' + test); | ||
} else { | ||
tests.splice(i, 1); | ||
} | ||
/* Build test object in the form: | ||
* [{ | ||
* fixture : 'filename.css', | ||
* expected : Boolean, | ||
* unused : Boolean | ||
* }, { | ||
* ... | ||
* }, ...] | ||
*/ | ||
tests = fixtures.map(function (test, i) { | ||
return { | ||
fixture : test, | ||
expected : expected.indexOf(test) === -1 ? null : true, | ||
unused : unused.indexOf(test) === -1 ? null : true, | ||
}; | ||
}); | ||
@@ -30,3 +44,6 @@ | ||
before(function (done) { | ||
uncss(rfs('index.html'), { csspath: 'tests' }, function (output) { | ||
uncss(rfs('selectors/index.html'), { csspath: 'tests/selectors' }, function (err, output) { | ||
if (err) { | ||
throw err; | ||
} | ||
rawcss = output; | ||
@@ -37,19 +54,16 @@ done(); | ||
/* Test that the CSS in the 'unused' folder is not included in the generated | ||
* CSS | ||
*/ | ||
tests.forEach(function (test) { | ||
it('Should not output unused ' + test.split('.')[0], function () { | ||
expect(rawcss).to.not.include.string(rfs('unused/' + test)); | ||
}); | ||
}); | ||
/* Test that the CSS in the 'expected' folder is included in the generated | ||
* CSS | ||
*/ | ||
tests.forEach(function (test) { | ||
it('Should output expected ' + test.split('.')[0], function () { | ||
expect(rawcss).to.include.string(rfs('expected/' + test)); | ||
}); | ||
if (test.expected) { | ||
it('Should output expected ' + test.fixture.split('.')[0], function () { | ||
expect(rawcss).to.include.string(rfs('selectors/expected/' + test.fixture)); | ||
}); | ||
} | ||
if (test.unused) { | ||
it('Should not output unused ' + test.fixture.split('.')[0], function () { | ||
expect(rawcss).to.not.include.string(rfs('selectors/unused/' + test.fixture)); | ||
}); | ||
} | ||
}); | ||
}); |
@@ -0,1 +1,2 @@ | ||
/* jshint expr: true */ | ||
var expect = require('chai').expect, | ||
@@ -12,3 +13,3 @@ fs = require('fs'), | ||
/* Used to check that all the requests to gh-pages generate the same CSS. | ||
* Expected to fail once if the gh-page is updated. | ||
* Expected to fail if the gh-page is updated. | ||
*/ | ||
@@ -26,4 +27,5 @@ before(function (done) { | ||
it('Accepts an array of urls', function (done) { | ||
this.timeout(15000); | ||
uncss(['http://getbootstrap.com/examples/jumbotron/'], function (output) { | ||
this.timeout(25000); | ||
uncss(['http://getbootstrap.com/examples/jumbotron/'], function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.have.length.above(2); | ||
@@ -35,4 +37,5 @@ fs.writeFile(__dirname + '/output/bootstrap/jumbotron.compiled.css', output, done); | ||
it('Deals with CSS files linked with absolute url', function (done) { | ||
this.timeout(15000); | ||
uncss(['http://giakki.github.io/uncss/'], function (output) { | ||
this.timeout(25000); | ||
uncss(['http://giakki.github.io/uncss/'], function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.equal(prev_run); | ||
@@ -45,23 +48,35 @@ prev_run = output; | ||
it('Deals with relative options.stylesheets when using urls', function (done) { | ||
this.timeout(15000); | ||
uncss(['http://giakki.github.io/uncss/'], { stylesheets: ['stylesheets/stylesheet.css'] }, function (output) { | ||
expect(output).to.equal(prev_run); | ||
prev_run = output; | ||
done(); | ||
}); | ||
this.timeout(25000); | ||
uncss( | ||
['http://giakki.github.io/uncss/'], | ||
{ stylesheets: ['//cdnjs.cloudflare.com/ajax/libs/colors/1.0/colors.min.css', | ||
'stylesheets/stylesheet.css'] }, | ||
function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.equal(prev_run); | ||
prev_run = output; | ||
done(); | ||
} | ||
); | ||
}); | ||
it('Deals with absolute options.stylesheets when using urls', function (done) { | ||
this.timeout(15000); | ||
uncss(['http://giakki.github.io/uncss/'], { stylesheets: ['/uncss/stylesheets/stylesheet.css'] }, function (output) { | ||
expect(output).to.equal(prev_run); | ||
prev_run = output; | ||
done(); | ||
}); | ||
this.timeout(25000); | ||
uncss( | ||
['http://giakki.github.io/uncss/'], | ||
{ stylesheets: ['//cdnjs.cloudflare.com/ajax/libs/colors/1.0/colors.min.css', | ||
'/uncss/stylesheets/stylesheet.css'] }, | ||
function (err, output) { | ||
expect(err).to.be.null; | ||
expect(output).to.equal(prev_run); | ||
prev_run = output; | ||
done(); | ||
} | ||
); | ||
}); | ||
after(function (done) { | ||
fs.writeFile(__dirname + '/output/bootstrap/jumbotron.compiled.css', prev_run, done); | ||
fs.writeFile(__dirname + '/output/gh-pages/stylesheets/stylesheet.css', prev_run, done); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
92772
75
1947
92
4
6