closure-linter-wrapper
Version:
Node Wrapper to allow access to Google Closure Linter from NodeJS
220 lines (184 loc) • 5.27 kB
JavaScript
/*
* closure-linter-wrapper
* https://github.com/jmendiara/node-closure-linter-wrapper
*
* Copyright (c) 2013 Javier Mendiara Cañardo
* Licensed under the MIT license.
*/
;
var exec = require('child_process').exec,
path = require('path'),
fileRegex = new RegExp('^\\x2D{5}\\s+FILE\\s+:\\s+(.+)\\s+\\x2D{5}$'),
errorRegex = new RegExp('^Line\\s(\\d+),\\sE:(-?[\\d]+):\\s(.*)$'),
abstractRegex = new RegExp('^Found ([\\d]+) errors?, including ([\\d]+) ' +
'new errors?, in ([\\d]+) files? \\(([\\d]+) files? OK\\).?$'),
successRegex = new RegExp('^([\\d]+) file.*no errors found.?$'),
skippingRegex = new RegExp('^Skipping ([\\d]+) file\\(s\\)\\.?$'),
checkedPythonPath = Object.create(null);
function createError(code, description, info) {
return {
code: code,
description: description,
info: info
};
}
function createErrorPythonNotFound(reason, err) {
return createError(1, reason, err);
}
function createErrorGJSLintFailed(match, fails) {
return createError(2, 'gjslint failed', {
fails: fails,
total: parseInt(match[1], 10),
newErrors: parseInt(match[2], 10),
filesCount: parseInt(match[3], 10),
filesOK: parseInt(match[4], 10)
});
}
function createParseErrorUnknown(lines) {
return createError(3, 'Parse failed', {
lines: lines
});
}
function createErrorExecutingClosureLinter(fullCommand, err) {
return createError(4, 'closure_linter execution failed',
fullCommand + '\n' + err);
}
function createFailObj(match) {
return {
file: match[1],
errors: []
};
}
function createErrorObj(match) {
return {
line: parseInt(match[1], 10),
code: parseInt(match[2], 10),
description: match[3]
};
}
function createParseSuccessObj(match) {
return {
filesCount: parseInt(match[1], 10)
};
}
var checkPython = function checkPython(pythonPath, callback) {
if (checkedPythonPath[pythonPath]) {
callback(null);
return;
}
exec(pythonPath + ' --version',
function(err, stdout, stderr) {
if (err !== null) {
if (err.code === 127) {
callback(createErrorPythonNotFound('Python not installed', err));
} else {
callback(createErrorPythonNotFound('', err));
}
} else {
checkedPythonPath[pythonPath] = true;
callback(null, stderr);
}
}
);
};
//FIXME: Think better this piece of shit
var parseResult = function parseResult(text, callback) {
var errors = [], i = 0, l, errBlock, match, match2, lines;
text = text.replace(/\r/g, '');
lines = text.split('\n');
match = skippingRegex.exec(lines[i]);
if (match) {
i++;
}
match = successRegex.exec(lines[i]);
if (match) {
callback(null, createParseSuccessObj(match));
return;
}
for (l = lines.length; i < l; i++) {
match = fileRegex.exec(lines[i]);
if (match) {
match2 = errorRegex.exec(lines[++i]);
errBlock = createFailObj(match);
for (var j = i; match2 && j < lines.length;) {
errBlock.errors.push(createErrorObj(match2));
match2 = errorRegex.exec(lines[++j]);
}
i = --j;
errors.push(errBlock);
} else {
match = abstractRegex.exec(lines[i]);
if (match) {
callback(createErrorGJSLintFailed(match, errors));
return;
}
}
}
callback(createParseErrorUnknown(lines));
};
function execute(program, options, callback) {
var pythonPath = options.pythonPath || 'python';
checkPython(pythonPath, function(err) {
if (err) {
callback(err);
return;
}
var flags = options.flags || [],
src = options.src || [],
params = [],
cmd = path.normalize(pythonPath + ' "' + __dirname +
'/../tools/' + program + '.py"'),
fullCommand;
params.push(cmd);
params.push(flags.join(' '));
params.push(src.join(' '));
fullCommand = params.join(' ');
exec(fullCommand, function(err, stdout, stderr) {
if (err && stderr !== '') {
callback(createErrorExecutingClosureLinter(fullCommand, stderr));
return;
}
//TODO: Move this management before execute call
if (program === 'gjslint') {
parseResult(stdout, callback);
} else {
callback(err, stdout);
}
});
});
}
/**
*
* @param {Object} options
* @param {Function} callback
*/
exports.gjslint = function(options, callback) {
execute('gjslint', options, function(err, result) {
if (options.reporter && options.reporter.name) {
var reporter = require('./reporter/' + options.reporter.name);
reporter.reportGJSLint(err, result, options.reporter);
}
callback(err, result);
});
};
/**
*
* @param {Object} options
* @param {Function} callback
*/
exports.fixjsstyle = function(options, callback) {
execute('fixjsstyle', options, function(err, result) {
if (options.reporter && options.reporter.name) {
var reporter = require('./reporter/' + options.reporter.name);
reporter.reportFixStyle(err, result, options.reporter);
}
callback(err, result);
});
};
//These funcs are private. exposed only for testing.
/**
*/
exports.parseResult = parseResult;
/**
*/
exports.checkPython = checkPython;