source: Dev/trunk/node_modules/grunt-contrib-jshint/tasks/lib/jshint.js @ 484

Last change on this file since 484 was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 6.6 KB
Line 
1/*
2 * grunt-contrib-jshint
3 * http://gruntjs.com/
4 *
5 * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
6 * Licensed under the MIT license.
7 */
8
9'use strict';
10
11var path = require('path');
12var jshint = require('jshint').JSHINT;
13var jshintcli = require('jshint/src/cli');
14
15exports.init = function(grunt) {
16  var exports = {
17    usingGruntReporter: false
18  };
19
20  // No idea why JSHint treats tabs as options.indent # characters wide, but it
21  // does. See issue: https://github.com/jshint/jshint/issues/430
22  var getTabStr = function(options) {
23    options = options ? grunt.util._.clone(options) : {};
24    options.maxerr = 50;
25    // Do something that's going to error.
26    jshint('\tx', options);
27    // If an error occurred, figure out what character JSHint reported and
28    // subtract one.
29    var character = jshint.errors && jshint.errors[0] && jshint.errors[0].character - 1;
30    // If character is actually a number, use it. Otherwise use 1.
31    var tabsize = isNaN(character) ? 1 : character;
32    // If tabsize > 1, return something that should be safe to use as a
33    // placeholder. \uFFFF repeated 2+ times.
34    return tabsize > 1 && grunt.util.repeat(tabsize, '\uFFFF');
35  };
36
37  var tabregex = /\t/g;
38
39  // Select a reporter (if not using the default Grunt reporter)
40  // Copied from jshint/src/cli/cli.js until that part is exposed
41  exports.selectReporter = function(options) {
42    switch (true) {
43    // JSLint reporter
44    case options.reporter === 'jslint':
45    case options['jslint-reporter']:
46      options.reporter = 'jshint/src/reporters/jslint_xml.js';
47      break;
48
49    // CheckStyle (XML) reporter
50    case options.reporter === 'checkstyle':
51    case options['checkstyle-reporter']:
52      options.reporter = 'jshint/src/reporters/checkstyle.js';
53      break;
54
55    // Reporter that displays additional JSHint data
56    case options['show-non-errors']:
57      options.reporter = 'jshint/src/reporters/non_error.js';
58      break;
59
60    // Custom reporter
61    case options.reporter !== undefined:
62      options.reporter = path.resolve(process.cwd(), options.reporter);
63    }
64
65    var reporter;
66    if (options.reporter) {
67      try {
68        reporter = require(options.reporter).reporter;
69        exports.usingGruntReporter = false;
70      } catch (err) {
71        grunt.fatal(err);
72      }
73    }
74
75    // Use the default Grunt reporter if none are found
76    if (!reporter) {
77      reporter = exports.reporter;
78      exports.usingGruntReporter = true;
79    }
80
81    return reporter;
82  };
83
84  // Default Grunt JSHint reporter
85  exports.reporter = function(results, data) {
86    // Dont report empty data as its an ignored file
87    if (data.length < 1) {
88      grunt.log.error('0 files linted. Please check your ignored files.');
89      return;
90    }
91
92    if (results.length === 0) {
93      // Success!
94      grunt.verbose.ok();
95      return;
96    }
97
98    var options = data[0].options;
99
100    // Tab size as reported by JSHint.
101    var tabstr = getTabStr(options);
102    var placeholderregex = new RegExp(tabstr, 'g');
103
104    // Iterate over all errors.
105    results.forEach(function(result) {
106      // Display the defending file
107      var msg = 'Linting' + (result.file ? ' ' + result.file : '') + ' ...';
108      grunt.verbose.write(msg);
109
110      // Something went wrong.
111      grunt.verbose.or.write(msg);
112      grunt.log.error();
113
114      var e = result.error;
115      // Sometimes there's no error object.
116      if (!e) { return; }
117      var pos;
118      var code = '';
119      var evidence = e.evidence;
120      var character = e.character;
121      if (evidence) {
122        // Manually increment errorcount since we're not using grunt.log.error().
123        grunt.fail.errorcount++;
124        // Descriptive code error.
125        pos = '['.red + ('L' + e.line).yellow + ':'.red + ('C' + character).yellow + ']'.red;
126        if (e.code) {
127          code = e.code.yellow + ':'.red + ' ';
128        }
129        grunt.log.writeln(pos + ' ' + code + e.reason.yellow);
130        // If necessary, eplace each tab char with something that can be
131        // swapped out later.
132        if (tabstr) {
133          evidence = evidence.replace(tabregex, tabstr);
134        }
135        if (character === 0) {
136          // Beginning of line.
137          evidence = '?'.inverse.red + evidence;
138        } else if (character > evidence.length) {
139          // End of line.
140          evidence = evidence + ' '.inverse.red;
141        } else {
142          // Middle of line.
143          evidence = evidence.slice(0, character - 1) + evidence[character - 1].inverse.red +
144            evidence.slice(character);
145        }
146        // Replace tab placeholder (or tabs) but with a 2-space soft tab.
147        evidence = evidence.replace(tabstr ? placeholderregex : tabregex, '  ');
148        grunt.log.writeln(evidence);
149      } else {
150        // Generic "Whoops, too many errors" error.
151        grunt.log.error(e.reason);
152      }
153    });
154    grunt.log.writeln();
155  };
156
157  // Run JSHint on the given files with the given options
158  exports.lint = function(files, options, done) {
159    var cliOptions = {
160      verbose: grunt.option('verbose'),
161      extensions: '',
162    };
163
164    // A list of non-dot-js extensions to check
165    if (options.extensions) {
166      cliOptions.extensions = options.extensions;
167      delete options.extensions;
168    }
169
170    // A list ignored files
171    if (options.ignores) {
172      if (typeof options.ignores === 'string') {
173        options.ignores = [options.ignores];
174      }
175      cliOptions.ignores = options.ignores;
176      delete options.ignores;
177    }
178
179    // Select a reporter to use
180    var reporter = exports.selectReporter(options);
181
182    // Remove bad options that may have came in from the cli
183    ['reporter', 'jslint-reporter', 'checkstyle-reporter', 'show-non-errors'].forEach(function(opt) {
184      if (options.hasOwnProperty(opt)) {
185        delete options[opt];
186      }
187    });
188
189    // Read JSHint options from a specified jshintrc file.
190    if (options.jshintrc) {
191      options = jshintcli.loadConfig(options.jshintrc);
192      delete options.jshintrc;
193    }
194
195    // Enable/disable debugging if option explicitly set.
196    if (grunt.option('debug') !== undefined) {
197      options.devel = options.debug = grunt.option('debug');
198      // Tweak a few things.
199      if (grunt.option('debug')) {
200        options.maxerr = Infinity;
201      }
202    }
203
204    cliOptions.config = options;
205
206    // Run JSHint on all file and collect results/data
207    var allResults = [];
208    var allData = [];
209    var cliopts = grunt.util._.clone(cliOptions);
210    cliopts.args = files;
211    cliopts.reporter = function(results, data) {
212      reporter(results, data);
213      allResults = allResults.concat(results);
214      allData = allData.concat(data);
215    };
216    jshintcli.run(cliopts);
217    done(allResults, allData);
218  };
219
220  return exports;
221};
Note: See TracBrowser for help on using the repository browser.