jshint.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * grunt-contrib-jshint
  3. * https://gruntjs.com/
  4. *
  5. * Copyright (c) 2016 "Cowboy" Ben Alman, contributors
  6. * Licensed under the MIT license.
  7. */
  8. 'use strict';
  9. var path = require('path');
  10. var chalk = require('chalk');
  11. var jshintcli = require('jshint/src/cli');
  12. exports.init = function(grunt) {
  13. var exports = {
  14. usingGruntReporter: false
  15. };
  16. var pad = function(msg, length) {
  17. while (msg.length < length) {
  18. msg = ' ' + msg;
  19. }
  20. return msg;
  21. };
  22. // Select a reporter (if not using the default Grunt reporter)
  23. // Copied from jshint/src/cli/cli.js until that part is exposed
  24. exports.selectReporter = function(options) {
  25. switch (true) {
  26. // JSLint reporter
  27. case options.reporter === 'jslint':
  28. case options['jslint-reporter']:
  29. options.reporter = 'jshint/src/reporters/jslint_xml.js';
  30. break;
  31. // CheckStyle (XML) reporter
  32. case options.reporter === 'checkstyle':
  33. case options['checkstyle-reporter']:
  34. options.reporter = 'jshint/src/reporters/checkstyle.js';
  35. break;
  36. // Reporter that displays additional JSHint data
  37. case options['show-non-errors']:
  38. options.reporter = 'jshint/src/reporters/non_error.js';
  39. break;
  40. // Custom reporter
  41. case options.reporter !== null && options.reporter !== undefined:
  42. options.reporter = path.resolve(process.cwd(), options.reporter.toString());
  43. }
  44. var reporter;
  45. if (options.reporter) {
  46. try {
  47. reporter = require(options.reporter).reporter;
  48. exports.usingGruntReporter = false;
  49. } catch (err) {
  50. grunt.fatal(err);
  51. }
  52. }
  53. // Use the default Grunt reporter if none are found
  54. if (!reporter) {
  55. reporter = exports.reporter;
  56. exports.usingGruntReporter = true;
  57. }
  58. return reporter;
  59. };
  60. // Default Grunt JSHint reporter
  61. exports.reporter = function(results, data) {
  62. // Dont report empty data as its an ignored file
  63. if (data.length < 1) {
  64. grunt.log.error('0 files linted. Please check your ignored files.');
  65. return;
  66. }
  67. if (results.length === 0) {
  68. // Success!
  69. grunt.verbose.ok();
  70. return;
  71. }
  72. var options = data[0].options;
  73. grunt.log.writeln();
  74. var lastfile = null;
  75. // Iterate over all errors.
  76. results.forEach(function(result) {
  77. // Only print file name once per error
  78. if (result.file !== lastfile) {
  79. grunt.log.writeln(chalk.bold(result.file ? ' ' + result.file : ''));
  80. }
  81. lastfile = result.file;
  82. var e = result.error;
  83. // Sometimes there's no error object.
  84. if (!e) {
  85. return;
  86. }
  87. if (e.evidence) {
  88. // Manually increment errorcount since we're not using grunt.log.error().
  89. grunt.fail.errorcount++;
  90. // No idea why JSHint treats tabs as options.indent # characters wide, but it
  91. // does. See issue: https://github.com/jshint/jshint/issues/430
  92. // Replacing tabs with appropriate spaces (i.e. columns) ensures that
  93. // caret will line up correctly.
  94. var evidence = e.evidence.replace(/\t/g, grunt.util.repeat(options.indent, ' '));
  95. grunt.log.writeln(pad(e.line.toString(), 7) + ' |' + chalk.gray(evidence));
  96. grunt.log.write(grunt.util.repeat(9, ' ') + grunt.util.repeat(e.character - 1, ' ') + '^ ');
  97. grunt.verbose.write('[' + e.code + '] ');
  98. grunt.log.writeln(e.reason);
  99. } else {
  100. // Generic "Whoops, too many errors" error.
  101. grunt.log.error(e.reason);
  102. }
  103. });
  104. grunt.log.writeln();
  105. };
  106. // Run JSHint on the given files with the given options
  107. exports.lint = function(files, options, done) {
  108. var cliOptions = {
  109. verbose: grunt.option('verbose'),
  110. extensions: ''
  111. };
  112. // A list of non-dot-js extensions to check
  113. if (options.extensions) {
  114. cliOptions.extensions = options.extensions;
  115. delete options.extensions;
  116. }
  117. // A list ignored files
  118. if (options.ignores) {
  119. if (typeof options.ignores === 'string') {
  120. options.ignores = [options.ignores];
  121. }
  122. cliOptions.ignores = options.ignores;
  123. delete options.ignores;
  124. }
  125. // Option to extract JS from HTML file
  126. if (options.extract) {
  127. cliOptions.extract = options.extract;
  128. delete options.extract;
  129. }
  130. var reporterOutputDir;
  131. // Get reporter output directory for relative paths in reporters
  132. if (options.hasOwnProperty('reporterOutput')) {
  133. if (options.reporterOutput) {
  134. reporterOutputDir = path.dirname(options.reporterOutput);
  135. }
  136. delete options.reporterOutput;
  137. }
  138. // Select a reporter to use
  139. var reporter = exports.selectReporter(options);
  140. // Remove bad options that may have came in from the cli
  141. ['reporter', 'reporterOutputRelative', 'jslint-reporter', 'checkstyle-reporter', 'show-non-errors'].forEach(function(opt) {
  142. if (options.hasOwnProperty(opt)) {
  143. delete options[opt];
  144. }
  145. });
  146. if (options.jshintrc === true) {
  147. // let jshint find the options itself
  148. delete cliOptions.config;
  149. } else if (options.jshintrc) {
  150. // Read JSHint options from a specified jshintrc file.
  151. cliOptions.config = jshintcli.loadConfig(options.jshintrc);
  152. } else {
  153. // Enable/disable debugging if option explicitly set.
  154. if (grunt.option('debug') !== undefined) {
  155. options.devel = options.debug = grunt.option('debug');
  156. // Tweak a few things.
  157. if (grunt.option('debug')) {
  158. options.maxerr = Infinity;
  159. }
  160. }
  161. // pass all of the remaining options directly to jshint
  162. cliOptions.config = options;
  163. }
  164. // Run JSHint on all file and collect results/data
  165. var allResults = [];
  166. var allData = [];
  167. cliOptions.args = files;
  168. cliOptions.reporter = function(results, data) {
  169. if (reporterOutputDir) {
  170. results.forEach(function(datum) {
  171. datum.file = path.relative(reporterOutputDir, datum.file);
  172. });
  173. }
  174. reporter(results, data, options);
  175. allResults = allResults.concat(results);
  176. allData = allData.concat(data);
  177. };
  178. jshintcli.run(cliOptions);
  179. done(allResults, allData);
  180. };
  181. return exports;
  182. };