Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Refactored code to improve testability #60

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
@@ -0,0 +1,3 @@
language: node_js
node_js:
- '0.10'
10 changes: 10 additions & 0 deletions Gruntfile.js
Expand Up @@ -47,6 +47,7 @@ module.exports = function (grunt) {
}
}
},

jshint: {
options: {
jshintrc: '.jshintrc'
Expand All @@ -56,11 +57,20 @@ module.exports = function (grunt) {
'tasks/**/*.js',
'tests/**/*.js'
]
},

mochaTest: {
options: {
reporter: 'spec'
},
src: ['test/**/*.spec.js']
}
});

grunt.loadTasks('tasks');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-mocha-test');

grunt.registerTask('test', ['jshint', 'mochaTest']);
grunt.registerTask('default', ['jshint', 'recess']);
};
9 changes: 7 additions & 2 deletions package.json
Expand Up @@ -18,7 +18,7 @@
},
"repository": "sindresorhus/grunt-recess",
"scripts": {
"test": "grunt"
"test": "grunt test"
},
"dependencies": {
"async": "^0.9.0",
Expand All @@ -28,8 +28,13 @@
"recess": "^1.1.6"
},
"devDependencies": {
"chai": "^1.9.1",
"grunt": "^0.4.2",
"grunt-contrib-jshint": "^0.10.0"
"grunt-contrib-jshint": "^0.10.0",
"grunt-mocha-test": "^0.11.0",
"rewire": "^2.1.0",
"sinon": "^1.10.3",
"sinon-chai": "^2.5.0"
},
"peerDependencies": {
"grunt": "^0.4.0"
Expand Down
173 changes: 173 additions & 0 deletions tasks/lib/RecessTask.js
@@ -0,0 +1,173 @@
'use strict';

// External dependencies
var recess = require('recess'),
grunt = require('grunt'),
async = require('async'),
maxmin = require('maxmin'),
logError = require(__dirname + '/util/log-error');

var RecessTask;

RecessTask = function(task)
{
// Store reference to original task
this.task = task;

// Merge task options with defaults
this.options = task.options(RecessTask.DEFAULT_OPTIONS);
};

/**
* Default options that will be merged with options specified in
* the original task.
*
* @type {*}
*/
RecessTask.DEFAULT_OPTIONS = {
banner: '',
compress: false,
footer: ''
};

/**
* @type {string}
*/
RecessTask.TASK_NAME = 'recess';

/**
* @type {string}
*/
RecessTask.TASK_DESCRIPTION = 'Lint and minify CSS and LESS using RECESS';

/**
* Static method for registering an instance of the task with Grunt.
*
* @param {*} grunt
*/
RecessTask.registerWithGrunt = function(grunt)
{
grunt.registerMultiTask(
RecessTask.TASK_NAME,
RecessTask.TASK_DESCRIPTION,
function() {
var task;

task = new RecessTask(this);

task.run();
});
};

RecessTask.prototype.run = function()
{
var cb = this.task.async();
var files = this.task.files;
var options = this.task.options(RecessTask.DEFAULT_OPTIONS);

var banner = grunt.template.process(options.banner);
var footer = grunt.template.process(options.footer);
var reporter = false;

if (!files.length) {
grunt.log.writeln('No destinations specified.');
return cb();
}

// hook the reporting in...
if (options.report && options.report.reporter) {
reporter = {};
reporter.proto = require(__dirname + '/reporters/' + options.report.reporter + '.js');
reporter.mapping = options.report.mapping ? grunt.file.readJSON(options.report.mapping) : {};
reporter.inst = new reporter.proto(reporter.mapping);
options.compress = false;
options.compile = true;
}

async.eachSeries(files, function (el, cb2) {
var dest = el.dest;

if (!el.src.length) {
grunt.warn('No existing source files for destination "' + dest + '".');
cb2();
return;
}

recess(el.src, options, function (err, data) {
var min = [];
var max = [];

if (err) {
err.forEach(logError);
}

if (reporter) {
reporter.inst.startReport();
}

data.forEach(function (item) {
if (item.options.compile || reporter) {
min.push(item.output);
max.push(item.data);
// Extract status and check
} else if (item.output[1] && item.output[1].indexOf('Perfect!') !== -1) {
grunt.log.writeln(item.output.join('\n'));
} else {
grunt.warn(item.output.join('\n'));
}

if (reporter) {
reporter.inst.startFile(dest);

if (item.definitions && item.definitions.length) {
// loop over definitions to get errors
item.definitions.forEach(function (definition) {
if (definition.errors && definition.errors.length) {
definition.errors.forEach(function (definitionErr) {
// report that error
reporter.inst.logError(definitionErr);
});
}
});
}

reporter.inst.endFile();
}
});

if (min.length) {
if (dest) {
// Concat files
grunt.file.write(dest, banner + min.join('\n\n') + footer);
grunt.log.writeln('File "' + dest + '" created.');

if (options.compress) {
grunt.log.writeln(maxmin(max.join('\n\n'), min.join('\n\n'), true));
}
} else {
grunt.warn('No destination specified. Required when options.compile is enabled.');
}
}

if (reporter) {
// Write report to the report file, if wanted
reporter.inst.endReport();
if (options.report.output) {
options.report.outputFile = grunt.template.process(options.report.output);
options.report.outputDir = require('path').dirname(options.report.outputFile);
if (!grunt.file.exists(options.report.outputDir)) {
grunt.file.mkdir(options.report.outputDir);
}
grunt.file.write(options.report.outputFile, reporter.inst.report);
grunt.log.ok('Report "' + options.report.outputFile + '" created.');
}
}

cb2();
});
}, function (err) {
cb(!err);
});
};

module.exports = RecessTask;
32 changes: 32 additions & 0 deletions tasks/lib/util/log-error.js
@@ -0,0 +1,32 @@
'use strict';

var grunt = require('grunt'),
chalk = require('chalk' ),
padLine = require(__dirname + '/pad-line');

var logError;

logError = function(err)
{
// RECESS doesn't log errors when `compile: true`
// Duplicate its error logging style
if (err.type === 'Parse') {
// parse error
grunt.log.error(chalk.red('Parser error') + (err.filename ? ' in ' + chalk.yellow(err.filename) : '') + '\n');
} else {
// other exception
grunt.log.error((err.name ? chalk.red(err.name) + ': ' : '') + err.message +
(err.filename ? ' in ' + chalk.yellow(err.filename) : '') + '\n');
}

// if extract - then log it
if (err.extract) {
err.extract.forEach(function (line, index) {
grunt.log.error(padLine(err.line + index) + line);
});
}

grunt.warn('');
};

module.exports = logError;
20 changes: 20 additions & 0 deletions tasks/lib/util/pad-line.js
@@ -0,0 +1,20 @@
'use strict';

var _ = require('lodash'),
chalk = require('chalk');

var padLine;

padLine = function(line)
{
var num = line + '. ';
var space = '';

_.times(10 - num.length, function () {
space += ' ';
});

return chalk.gray(space + num);
};

module.exports = padLine;