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