diff --git a/bin/fixme b/bin/fixme index f60c1c9..306f2f4 100755 --- a/bin/fixme +++ b/bin/fixme @@ -1,7 +1,7 @@ #!/usr/bin/env node var fs = require('fs'); -var FixMe = require('../lib/fix-me'); +var FixMe = require('../lib/fixme'); var config; fs.readFile('/config.json', function(err, data) { @@ -9,5 +9,6 @@ fs.readFile('/config.json', function(err, data) { config = JSON.parse(data); } - new FixMe().run(config) + var fixer = new FixMe(); + fixer.run(config); }); diff --git a/lib/fix-me.js b/lib/fix-me.js deleted file mode 100644 index 2a06012..0000000 --- a/lib/fix-me.js +++ /dev/null @@ -1,88 +0,0 @@ -var readline = require('readline'); -var spawn = require('child_process').spawn; -var fs = require('fs'); - -var DEFAULT_PATHS = ['./']; -var DEFAULT_STRINGS = ['BUG', 'FIXME', 'HACK', 'TODO', 'XXX']; -var GREP_OPTIONS = [ - '--binary-files=without-match', - '--extended-regexp', - '--line-number', - '--only-matching', - '--recursive', - '--with-filename', - '--word-regexp', -]; - -function FixMe(writable) { - this.output = writable || process.stdout; -} - -FixMe.prototype.run = function(engineConfig) { - var paths, strings; - - if (engineConfig) { - paths = engineConfig.include_paths; - } else { - paths = DEFAULT_PATHS; - } - - if (engineConfig && engineConfig.config && engineConfig.config.strings) { - strings = engineConfig.config.strings; - } else { - strings = DEFAULT_STRINGS; - } - - this.find(paths, strings); -}; - -var isItsOwnConfigFile = function(path) { - return path.indexOf(".codeclimate.yml") !== -1; -}; - -var isAYamlComment = function(path, lineNumber) { - var lines = fs.readFileSync(path, "utf8").split("\n"); - var line = lines[lineNumber - 1] || ""; - return line.match(/^\s*#/); -}; - -FixMe.prototype.find = function(paths, strings, callback) { - var pattern = `(${strings.join('|')})`; - var grep = spawn('grep', [...GREP_OPTIONS, pattern, ...paths]); - - readline.createInterface({ input: grep.stdout }).on('line', (line) => { - var parts = line.split(':'); - var path = parts[0].replace(/^\/code\//, ''); - var lineNumber = parseInt(parts[1], 10); - var matchedString = parts[2]; - - if (!path || !lineNumber || !matchedString) { - process.stderr.write("Ignoring malformed output: " + line + "\n"); - return; - } - - if(isItsOwnConfigFile(path) && !isAYamlComment(path, lineNumber)) { return; } - - var issue = { - 'categories': ['Bug Risk'], - 'check_name': matchedString, - 'description': `${matchedString} found`, - 'location': { - 'lines': { - 'begin': lineNumber, - 'end': lineNumber, - }, - 'path': path, - }, - 'type': 'issue', - }; - - this.output.write(JSON.stringify(issue) + '\0'); - }); - - if (callback) { - grep.stdout.on('close', _ => callback()); - } -}; - -module.exports = FixMe; diff --git a/lib/fixme.js b/lib/fixme.js new file mode 100644 index 0000000..4d82287 --- /dev/null +++ b/lib/fixme.js @@ -0,0 +1,103 @@ +const readline = require('readline'); +const { spawn } = require('child_process'); +const fs = require('fs'); + +const DEFAULT_PATHS = ['./']; +const DEFAULT_STRINGS = ['BUG', 'FIXME', 'HACK', 'TODO', 'XXX']; +const GREP_OPTIONS = [ + '--binary-files=without-match', + '--extended-regexp', + '--line-number', + '--only-matching', + '--recursive', + '--with-filename', + '--word-regexp', +]; + +class FixMe { + constructor(writable) { + this.output = writable || process.stdout; + this.maxPathLength = 4; // initial length of "Path" + this.maxLineLength = 4; // initial length of "Line" + this.maxTypeLength = 4; // initial length of "Type" + this.issues = []; + } + + run(engineConfig) { + const outputPathType = process.argv.includes('--json') + ? 'json' + : process.argv.includes('--table') + ? 'table' + : 'default'; + + if (outputPathType === 'default' || process.argv.includes('--help')) { + console.log('Usage: fixme [OPTIONS] [PATH]\n\nOptions:\n --json\tOutput results in JSON format.\n --table\tOutput results in table format.\n --help\tShow help.'); + return; + } + + let paths = DEFAULT_PATHS; + if (engineConfig && engineConfig.include_paths) { + paths = engineConfig.include_paths; + } else if (process.argv.length > 3) { + paths = process.argv.slice(3); + } + + const strings = (engineConfig && engineConfig.config && engineConfig.config.strings) || DEFAULT_STRINGS; + + this.find(paths, strings, outputPathType); + } + + find(paths, strings, outputPathType, callback) { + const pattern = `(${strings.join('|')})`; + const grep = spawn('grep', [...GREP_OPTIONS, pattern, ...paths]); + + readline.createInterface({ input: grep.stdout }).on('line', (line) => { + const [fullPath, lineStr, matchedString] = line.split(':'); + const path = fullPath.replace(/^\/code\//, ''); + const lineNumber = parseInt(lineStr, 10); + + if (!path || !lineNumber || !matchedString) { + process.stderr.write(`Ignoring malformed output: ${line}\n`); + return; + } + + // Update the maximum widths for each column for better formatting + this.maxPathLength = Math.max(this.maxPathLength, path.length); + this.maxLineLength = Math.max(this.maxLineLength, `${lineNumber}`.length); + this.maxTypeLength = Math.max(this.maxTypeLength, matchedString.length); + + const issue = { + 'categories': ['Bug Risk'], + 'check_name': matchedString, + 'description': `${matchedString} found`, + 'location': { + 'lines': { + 'begin': lineNumber, + 'end': lineNumber, + }, + 'path': path, + }, + 'type': 'issue', + }; + + this.issues.push(issue); + }); + + grep.stdout.on('close', () => { + if (outputPathType === 'json') { + this.output.write(JSON.stringify(this.issues)); + } else if (outputPathType === 'table') { + // Now that we've gathered all issues, print headers with appropriate padding + console.log(`| ${'Path'.padEnd(this.maxPathLength, ' ')} | ${'Line'.padEnd(this.maxLineLength, ' ')} | ${'Type'.padEnd(this.maxTypeLength, ' ')} |`); + console.log(`| ${'-'.repeat(this.maxPathLength)} | ${'-'.repeat(this.maxLineLength)} | ${'-'.repeat(this.maxTypeLength)} |`); + + for (const issue of this.issues) { + console.log(`| ${issue.location.path.padEnd(this.maxPathLength, ' ')} | ${issue.location.lines.begin.toString().padEnd(this.maxLineLength, ' ')} | ${issue.check_name.padEnd(this.maxTypeLength, ' ')} |`); + } + } + if (callback) callback(); + }); + } +} + +module.exports = FixMe; diff --git a/package.json b/package.json index 62dbccd..90f4b95 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,10 @@ "name": "codeclimate-fixme", "description": "Static analysis tool that finds FIXME, TODO, BUG, etc. comments in your code.", "version": "0.0.1", - "main": "./lib/fix-me.js", + "main": "./lib/fixme.js", + "bin": { + "fixme": "./bin/fixme" + }, "devDependencies": { "chai": "3.4.1", "mocha": "2.3.3" diff --git a/test/fix-me.js b/test/fixme.js similarity index 98% rename from test/fix-me.js rename to test/fixme.js index 3340c85..f348c58 100644 --- a/test/fix-me.js +++ b/test/fixme.js @@ -1,8 +1,8 @@ /* global define, it, describe, context */ var expect = require('chai').expect; -var FixMe = require('../lib/fix-me.js'); -var IssueBuffer = require('./support/issue_buffer'); +var FixMe = require('../lib/fixme.js'); +var IssueBuffer = require('./support/issue_buffer.js'); describe("fixMe", function(){ describe("#run(engineConfig)", function() {
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: