eslint 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/conf/eslint.json +3 -0
- package/lib/cli-engine.js +74 -74
- package/lib/cli.js +12 -10
- package/lib/eslint.js +15 -26
- package/lib/logging.js +25 -0
- package/lib/options.js +7 -2
- package/lib/rules/array-bracket-spacing.js +2 -2
- package/lib/rules/arrow-body-style.js +71 -0
- package/lib/rules/comma-dangle.js +26 -10
- package/lib/rules/comma-spacing.js +72 -36
- package/lib/rules/eol-last.js +10 -4
- package/lib/rules/indent.js +8 -7
- package/lib/rules/key-spacing.js +13 -25
- package/lib/rules/linebreak-style.js +45 -10
- package/lib/rules/max-nested-callbacks.js +1 -1
- package/lib/rules/no-arrow-condition.js +88 -0
- package/lib/rules/no-case-declarations.js +47 -0
- package/lib/rules/no-extend-native.js +3 -3
- package/lib/rules/no-magic-numbers.js +22 -5
- package/lib/rules/no-mixed-spaces-and-tabs.js +23 -19
- package/lib/rules/no-multiple-empty-lines.js +39 -13
- package/lib/rules/no-plusplus.js +22 -1
- package/lib/rules/no-shadow.js +22 -4
- package/lib/rules/no-use-before-define.js +1 -1
- package/lib/rules/no-warning-comments.js +1 -1
- package/lib/rules/radix.js +36 -6
- package/lib/rules/space-in-parens.js +148 -199
- package/lib/rules/spaced-comment.js +3 -3
- package/lib/rules/valid-jsdoc.js +36 -19
- package/lib/rules.js +13 -9
- package/lib/testers/rule-tester.js +62 -7
- package/lib/util/estraverse.js +54 -0
- package/lib/util/glob-util.js +149 -0
- package/lib/util/source-code-fixer.js +1 -1
- package/lib/util/source-code.js +11 -1
- package/lib/util.js +15 -9
- package/package.json +21 -21
package/README.md
CHANGED
@@ -69,6 +69,7 @@ These folks keep the project moving and are resources for help:
|
|
69
69
|
* Ian VanSchooten ([@ianvs](https://github.com/ianvs)) - committer
|
70
70
|
* Toru Nagashima ([@mysticatea](https://github.com/mysticatea)) - committer
|
71
71
|
* Burak Yiğit Kaya ([@byk](https://github.com/byk)) - committer
|
72
|
+
* Alberto Rodríguez ([@alberto](https://github.com/alberto)) - committer
|
72
73
|
|
73
74
|
## Releases
|
74
75
|
|
package/conf/eslint.json
CHANGED
@@ -4,8 +4,10 @@
|
|
4
4
|
"rules": {
|
5
5
|
"no-alert": 0,
|
6
6
|
"no-array-constructor": 0,
|
7
|
+
"no-arrow-condition": 0,
|
7
8
|
"no-bitwise": 0,
|
8
9
|
"no-caller": 0,
|
10
|
+
"no-case-declarations": 0,
|
9
11
|
"no-catch-shadow": 0,
|
10
12
|
"no-class-assign": 0,
|
11
13
|
"no-cond-assign": 2,
|
@@ -110,6 +112,7 @@
|
|
110
112
|
"no-magic-numbers": 0,
|
111
113
|
|
112
114
|
"array-bracket-spacing": [0, "never"],
|
115
|
+
"arrow-body-style": [0, "as-needed"],
|
113
116
|
"arrow-parens": 0,
|
114
117
|
"arrow-spacing": 0,
|
115
118
|
"accessor-pairs": 0,
|
package/lib/cli-engine.js
CHANGED
@@ -22,7 +22,6 @@ var fs = require("fs"),
|
|
22
22
|
|
23
23
|
assign = require("object-assign"),
|
24
24
|
debug = require("debug"),
|
25
|
-
glob = require("glob"),
|
26
25
|
shell = require("shelljs"),
|
27
26
|
|
28
27
|
rules = require("./rules"),
|
@@ -31,6 +30,7 @@ var fs = require("fs"),
|
|
31
30
|
Config = require("./config"),
|
32
31
|
util = require("./util"),
|
33
32
|
fileEntryCache = require("file-entry-cache"),
|
33
|
+
globUtil = require("./util/glob-util"),
|
34
34
|
SourceCodeFixer = require("./util/source-code-fixer"),
|
35
35
|
validator = require("./config-validator"),
|
36
36
|
stringify = require("json-stable-stringify"),
|
@@ -90,6 +90,10 @@ var defaultOptions = {
|
|
90
90
|
ignorePath: null,
|
91
91
|
parser: DEFAULT_PARSER,
|
92
92
|
cache: false,
|
93
|
+
// in order to honor the cacheFile option if specified
|
94
|
+
// this option should not have a default value otherwise
|
95
|
+
// it will always be used
|
96
|
+
cacheLocation: "",
|
93
97
|
cacheFile: ".eslintcache",
|
94
98
|
fix: false
|
95
99
|
},
|
@@ -293,51 +297,78 @@ function isErrorMessage(message) {
|
|
293
297
|
return message.severity === 2;
|
294
298
|
}
|
295
299
|
|
300
|
+
/**
|
301
|
+
* create a md5Hash of a given string
|
302
|
+
* @param {string} str the string to calculate the hash for
|
303
|
+
* @returns {string} the calculated hash
|
304
|
+
*/
|
305
|
+
function md5Hash(str) {
|
306
|
+
return crypto
|
307
|
+
.createHash("md5")
|
308
|
+
.update(str, "utf8")
|
309
|
+
.digest("hex");
|
310
|
+
}
|
296
311
|
|
297
312
|
/**
|
298
|
-
*
|
299
|
-
*
|
313
|
+
* return the cacheFile to be used by eslint, based on whether the provided parameter is
|
314
|
+
* a directory or looks like a directory (ends in `path.sep`), in which case the file
|
315
|
+
* name will be the `cacheFile/.cache_hashOfCWD`
|
300
316
|
*
|
301
|
-
*
|
302
|
-
* directory where as our previous implementation without `glob` simply walked
|
303
|
-
* a directory that is passed. So this is to maintain backwards compatibility.
|
317
|
+
* if cacheFile points to a file or looks like a file then in will just use that file
|
304
318
|
*
|
305
|
-
*
|
306
|
-
*
|
307
|
-
* @param {string[]} [extensions] An array of accepted extensions
|
308
|
-
* @returns {Function} A function that takes a pathname and returns a glob that
|
309
|
-
* matches all files with the provided extensions if
|
310
|
-
* pathname is a directory.
|
319
|
+
* @param {string} cacheFile The name of file to be used to store the cache
|
320
|
+
* @returns {string} the resolved path to the cache file
|
311
321
|
*/
|
312
|
-
function
|
313
|
-
|
322
|
+
function getCacheFile(cacheFile) {
|
323
|
+
// make sure the path separators are normalized for the environment/os
|
324
|
+
// keeping the trailing path separator if present
|
325
|
+
cacheFile = path.normalize(cacheFile);
|
314
326
|
|
315
|
-
|
316
|
-
|
317
|
-
suffix += "/*." + extensions[0];
|
318
|
-
} else {
|
319
|
-
suffix += "/*.{" + extensions.join(",") + "}";
|
320
|
-
}
|
321
|
-
}
|
327
|
+
var resolvedCacheFile = path.resolve(cacheFile);
|
328
|
+
var looksLikeADirectory = cacheFile[cacheFile.length - 1 ] === path.sep;
|
322
329
|
|
323
330
|
/**
|
324
|
-
*
|
325
|
-
*
|
326
|
-
* @param {string} pathname The directory path to be modified
|
327
|
-
* @returns {string} The glob path or the file path itself
|
328
|
-
* @private
|
331
|
+
* return the name for the cache file in case the provided parameter is a directory
|
332
|
+
* @returns {string} the resolved path to the cacheFile
|
329
333
|
*/
|
330
|
-
|
331
|
-
|
334
|
+
function getCacheFileForDirectory() {
|
335
|
+
return path.join(resolvedCacheFile, ".cache_" + md5Hash(process.cwd()));
|
336
|
+
}
|
332
337
|
|
333
|
-
|
334
|
-
|
338
|
+
var fileStats;
|
339
|
+
|
340
|
+
try {
|
341
|
+
fileStats = fs.lstatSync(resolvedCacheFile);
|
342
|
+
} catch (ex) {
|
343
|
+
fileStats = null;
|
344
|
+
}
|
345
|
+
|
346
|
+
|
347
|
+
// in case the file exists we need to verify if the provided path
|
348
|
+
// is a directory or a file. If it is a directory we want to create a file
|
349
|
+
// inside that directory
|
350
|
+
if (fileStats) {
|
351
|
+
// is a directory or is a file, but the original file the user provided
|
352
|
+
// looks like a directory but `path.resolve` removed the `last path.sep`
|
353
|
+
// so we need to still treat this like a directory
|
354
|
+
if (fileStats.isDirectory() || looksLikeADirectory) {
|
355
|
+
return getCacheFileForDirectory();
|
335
356
|
}
|
357
|
+
// is file so just use that file
|
358
|
+
return resolvedCacheFile;
|
359
|
+
}
|
336
360
|
|
337
|
-
|
338
|
-
|
339
|
-
|
361
|
+
// here we known the file or directory doesn't exist,
|
362
|
+
// so we will try to infer if its a directory if it looks like a directory
|
363
|
+
// for the current operating system.
|
340
364
|
|
365
|
+
// if the last character passed is a path separator we assume is a directory
|
366
|
+
if (looksLikeADirectory) {
|
367
|
+
return getCacheFileForDirectory();
|
368
|
+
}
|
369
|
+
|
370
|
+
return resolvedCacheFile;
|
371
|
+
}
|
341
372
|
|
342
373
|
//------------------------------------------------------------------------------
|
343
374
|
// Public Interface
|
@@ -356,12 +387,15 @@ function CLIEngine(options) {
|
|
356
387
|
*/
|
357
388
|
this.options = assign(Object.create(defaultOptions), options || {});
|
358
389
|
|
390
|
+
|
391
|
+
var cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile);
|
392
|
+
|
359
393
|
/**
|
360
394
|
* cache used to not operate on files that haven't changed since last successful
|
361
395
|
* execution (e.g. file passed with no errors and no warnings
|
362
396
|
* @type {Object}
|
363
397
|
*/
|
364
|
-
this._fileCache = fileEntryCache.create(
|
398
|
+
this._fileCache = fileEntryCache.create(cacheFile); // eslint-disable-line no-underscore-dangle
|
365
399
|
|
366
400
|
if (!this.options.cache) {
|
367
401
|
this._fileCache.destroy(); // eslint-disable-line no-underscore-dangle
|
@@ -478,11 +512,7 @@ CLIEngine.prototype = {
|
|
478
512
|
* @returns {string[]} The equivalent glob patterns.
|
479
513
|
*/
|
480
514
|
resolveFileGlobPatterns: function(patterns) {
|
481
|
-
|
482
|
-
return ext.charAt(0) === "." ? ext.substr(1) : ext;
|
483
|
-
});
|
484
|
-
|
485
|
-
return patterns.map(processPath(extensions));
|
515
|
+
return globUtil.resolveFileGlobPatterns(patterns, this.options.extensions);
|
486
516
|
},
|
487
517
|
|
488
518
|
/**
|
@@ -496,32 +526,13 @@ CLIEngine.prototype = {
|
|
496
526
|
options = this.options,
|
497
527
|
fileCache = this._fileCache, // eslint-disable-line no-underscore-dangle
|
498
528
|
configHelper = new Config(options),
|
499
|
-
ignoredPaths = IgnoredPaths.load(options),
|
500
|
-
ignoredPathsList = ignoredPaths.patterns || [],
|
501
|
-
globOptions,
|
502
529
|
stats,
|
503
530
|
startTime,
|
504
531
|
prevConfig; // the previous configuration used
|
505
532
|
|
506
533
|
startTime = Date.now();
|
507
|
-
patterns = this.resolveFileGlobPatterns(patterns);
|
508
534
|
|
509
|
-
|
510
|
-
nodir: true,
|
511
|
-
ignore: ignoredPathsList
|
512
|
-
};
|
513
|
-
|
514
|
-
/**
|
515
|
-
* create a md5Hash of a given string
|
516
|
-
* @param {string} str the string to calculate the hash for
|
517
|
-
* @returns {string} the calculated hash
|
518
|
-
*/
|
519
|
-
function md5Hash(str) {
|
520
|
-
return crypto
|
521
|
-
.createHash("md5")
|
522
|
-
.update(str)
|
523
|
-
.digest("hex");
|
524
|
-
}
|
535
|
+
patterns = this.resolveFileGlobPatterns(patterns);
|
525
536
|
|
526
537
|
/**
|
527
538
|
* Calculates the hash of the config file used to validate a given file
|
@@ -552,22 +563,16 @@ CLIEngine.prototype = {
|
|
552
563
|
/**
|
553
564
|
* Executes the linter on a file defined by the `filename`. Skips
|
554
565
|
* unsupported file extensions and any files that are already linted.
|
555
|
-
* @param {string} filename The file to be linted
|
566
|
+
* @param {string} filename The resolved filename of the file to be linted
|
556
567
|
* @returns {void}
|
557
568
|
*/
|
558
569
|
function executeOnFile(filename) {
|
559
|
-
var
|
560
|
-
if (shouldIgnore) {
|
561
|
-
return;
|
562
|
-
}
|
570
|
+
var hashOfConfig;
|
563
571
|
|
564
|
-
filename = fs.realpathSync(filename);
|
565
572
|
if (processed[filename]) {
|
566
573
|
return;
|
567
574
|
}
|
568
575
|
|
569
|
-
var hashOfConfig;
|
570
|
-
|
571
576
|
if (options.cache) {
|
572
577
|
// get the descriptor for this file
|
573
578
|
// with the metadata and the flag that determines if
|
@@ -623,13 +628,8 @@ CLIEngine.prototype = {
|
|
623
628
|
results.push(res);
|
624
629
|
}
|
625
630
|
|
626
|
-
|
627
|
-
|
628
|
-
executeOnFile(pattern);
|
629
|
-
} else {
|
630
|
-
glob.sync(pattern, globOptions).forEach(executeOnFile);
|
631
|
-
}
|
632
|
-
});
|
631
|
+
// Lint each desired file
|
632
|
+
globUtil.listFilesToProcess(patterns, options).forEach(executeOnFile);
|
633
633
|
|
634
634
|
// only warn for files explicitly passed on the command line
|
635
635
|
if (options.ignore) {
|
package/lib/cli.js
CHANGED
@@ -22,7 +22,8 @@ var fs = require("fs"),
|
|
22
22
|
|
23
23
|
options = require("./options"),
|
24
24
|
CLIEngine = require("./cli-engine"),
|
25
|
-
mkdirp = require("mkdirp")
|
25
|
+
mkdirp = require("mkdirp"),
|
26
|
+
log = require("./logging");
|
26
27
|
|
27
28
|
//------------------------------------------------------------------------------
|
28
29
|
// Helpers
|
@@ -52,6 +53,7 @@ function translateOptions(cliOptions) {
|
|
52
53
|
parser: cliOptions.parser,
|
53
54
|
cache: cliOptions.cache,
|
54
55
|
cacheFile: cliOptions.cacheFile,
|
56
|
+
cacheLocation: cliOptions.cacheLocation,
|
55
57
|
fix: cliOptions.fix
|
56
58
|
};
|
57
59
|
}
|
@@ -72,7 +74,7 @@ function printResults(engine, results, format, outputFile) {
|
|
72
74
|
|
73
75
|
formatter = engine.getFormatter(format);
|
74
76
|
if (!formatter) {
|
75
|
-
|
77
|
+
log.error("Could not find formatter '%s'.", format);
|
76
78
|
return false;
|
77
79
|
}
|
78
80
|
|
@@ -83,7 +85,7 @@ function printResults(engine, results, format, outputFile) {
|
|
83
85
|
filePath = path.resolve(process.cwd(), outputFile);
|
84
86
|
|
85
87
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
|
86
|
-
|
88
|
+
log.error("Cannot write to output file path, it is a directory: %s", outputFile);
|
87
89
|
return false;
|
88
90
|
}
|
89
91
|
|
@@ -91,11 +93,11 @@ function printResults(engine, results, format, outputFile) {
|
|
91
93
|
mkdirp.sync(path.dirname(filePath));
|
92
94
|
fs.writeFileSync(filePath, output);
|
93
95
|
} catch (ex) {
|
94
|
-
|
96
|
+
log.error("There was a problem writing the output file:\n%s", ex);
|
95
97
|
return false;
|
96
98
|
}
|
97
99
|
} else {
|
98
|
-
|
100
|
+
log.info(output);
|
99
101
|
}
|
100
102
|
}
|
101
103
|
|
@@ -130,7 +132,7 @@ var cli = {
|
|
130
132
|
try {
|
131
133
|
currentOptions = options.parse(args);
|
132
134
|
} catch (error) {
|
133
|
-
|
135
|
+
log.error(error.message);
|
134
136
|
return 1;
|
135
137
|
}
|
136
138
|
|
@@ -138,11 +140,11 @@ var cli = {
|
|
138
140
|
|
139
141
|
if (currentOptions.version) { // version from package.json
|
140
142
|
|
141
|
-
|
143
|
+
log.info("v" + require("../package.json").version);
|
142
144
|
|
143
145
|
} else if (currentOptions.help || (!files.length && !text)) {
|
144
146
|
|
145
|
-
|
147
|
+
log.info(options.generateHelp());
|
146
148
|
|
147
149
|
} else {
|
148
150
|
|
@@ -150,7 +152,7 @@ var cli = {
|
|
150
152
|
|
151
153
|
// disable --fix for piped-in code until we know how to do it correctly
|
152
154
|
if (text && currentOptions.fix) {
|
153
|
-
|
155
|
+
log.error("The --fix option is not available for piped-in code.");
|
154
156
|
return 1;
|
155
157
|
}
|
156
158
|
|
@@ -171,7 +173,7 @@ var cli = {
|
|
171
173
|
tooManyWarnings = currentOptions.maxWarnings >= 0 && report.warningCount > currentOptions.maxWarnings;
|
172
174
|
|
173
175
|
if (!report.errorCount && tooManyWarnings) {
|
174
|
-
|
176
|
+
log.error("ESLint found too many warnings (maximum: %s).", currentOptions.maxWarnings);
|
175
177
|
}
|
176
178
|
|
177
179
|
return (report.errorCount || tooManyWarnings) ? 1 : 0;
|
package/lib/eslint.js
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
// Requirements
|
11
11
|
//------------------------------------------------------------------------------
|
12
12
|
|
13
|
-
var estraverse = require("estraverse
|
13
|
+
var estraverse = require("./util/estraverse"),
|
14
14
|
escope = require("escope"),
|
15
15
|
environments = require("../conf/environments"),
|
16
16
|
blankScriptAST = require("../conf/blank-script.json"),
|
@@ -32,17 +32,6 @@ var DEFAULT_PARSER = require("../conf/eslint.json").parser;
|
|
32
32
|
// Helpers
|
33
33
|
//------------------------------------------------------------------------------
|
34
34
|
|
35
|
-
// additional changes to make estraverse happy
|
36
|
-
estraverse.Syntax.ExperimentalSpreadProperty = "ExperimentalSpreadProperty";
|
37
|
-
estraverse.Syntax.ExperimentalRestProperty = "ExperimentalRestProperty";
|
38
|
-
|
39
|
-
estraverse.VisitorKeys.ExperimentalSpreadProperty = ["argument"];
|
40
|
-
estraverse.VisitorKeys.ExperimentalRestProperty = ["argument"];
|
41
|
-
|
42
|
-
// All nodes in ObjectExpression.properties and ObjectPattern.properties are visited as `Property`.
|
43
|
-
// See Also: https://github.com/estools/estraverse/blob/master/estraverse.js#L687-L688
|
44
|
-
estraverse.VisitorKeys.Property.push("argument");
|
45
|
-
|
46
35
|
/**
|
47
36
|
* Parses a list of "name:boolean_value" or/and "name" options divided by comma or
|
48
37
|
* whitespace.
|
@@ -637,6 +626,20 @@ module.exports = (function() {
|
|
637
626
|
this.reset();
|
638
627
|
}
|
639
628
|
|
629
|
+
// search and apply "eslint-env *".
|
630
|
+
var envInFile = findEslintEnv(text || textOrSourceCode.text);
|
631
|
+
if (envInFile) {
|
632
|
+
if (!config || !config.env) {
|
633
|
+
config = assign({}, config || {}, {env: envInFile});
|
634
|
+
} else {
|
635
|
+
config = assign({}, config);
|
636
|
+
config.env = assign({}, config.env, envInFile);
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
// process initial config to make it safe to extend
|
641
|
+
config = prepareConfig(config || {});
|
642
|
+
|
640
643
|
// only do this for text
|
641
644
|
if (text !== null) {
|
642
645
|
|
@@ -646,20 +649,6 @@ module.exports = (function() {
|
|
646
649
|
return messages;
|
647
650
|
}
|
648
651
|
|
649
|
-
// search and apply "eslint-env *".
|
650
|
-
var envInFile = findEslintEnv(text);
|
651
|
-
if (envInFile) {
|
652
|
-
if (!config || !config.env) {
|
653
|
-
config = assign({}, config || {}, {env: envInFile});
|
654
|
-
} else {
|
655
|
-
config = assign({}, config);
|
656
|
-
config.env = assign({}, config.env, envInFile);
|
657
|
-
}
|
658
|
-
}
|
659
|
-
|
660
|
-
// process initial config to make it safe to extend
|
661
|
-
config = prepareConfig(config || {});
|
662
|
-
|
663
652
|
ast = parse(text.replace(/^#!([^\r\n]+)/, function(match, captured) {
|
664
653
|
shebang = captured;
|
665
654
|
return "//" + captured;
|
package/lib/logging.js
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Handle logging for Eslint
|
3
|
+
* @author Gyandeep Singh
|
4
|
+
* @copyright 2015 Gyandeep Singh. All rights reserved.
|
5
|
+
*/
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
/* istanbul ignore next */
|
9
|
+
module.exports = {
|
10
|
+
/**
|
11
|
+
* Cover for console.log
|
12
|
+
* @returns {void}
|
13
|
+
*/
|
14
|
+
info: function() {
|
15
|
+
console.log.apply(console, Array.prototype.slice.call(arguments));
|
16
|
+
},
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Cover for console.error
|
20
|
+
* @returns {void}
|
21
|
+
*/
|
22
|
+
error: function() {
|
23
|
+
console.error.apply(console, Array.prototype.slice.call(arguments));
|
24
|
+
}
|
25
|
+
};
|
package/lib/options.js
CHANGED
@@ -69,9 +69,14 @@ module.exports = optionator({
|
|
69
69
|
},
|
70
70
|
{
|
71
71
|
option: "cache-file",
|
72
|
-
type: "String",
|
72
|
+
type: "path::String",
|
73
73
|
default: ".eslintcache",
|
74
|
-
description: "Path to the cache file"
|
74
|
+
description: "Path to the cache file. Deprecated: use --cache-location"
|
75
|
+
},
|
76
|
+
{
|
77
|
+
option: "cache-location",
|
78
|
+
type: "path::String",
|
79
|
+
description: "Path to the cache file or directory"
|
75
80
|
},
|
76
81
|
{
|
77
82
|
heading: "Specifying rules and plugins"
|
@@ -116,7 +116,7 @@ module.exports = function(context) {
|
|
116
116
|
* @returns {boolean} Whether or not the node is an object type.
|
117
117
|
*/
|
118
118
|
function isObjectType(node) {
|
119
|
-
return node.type === "ObjectExpression" || node.type === "ObjectPattern";
|
119
|
+
return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
|
120
120
|
}
|
121
121
|
|
122
122
|
/**
|
@@ -125,7 +125,7 @@ module.exports = function(context) {
|
|
125
125
|
* @returns {boolean} Whether or not the node is an array type.
|
126
126
|
*/
|
127
127
|
function isArrayType(node) {
|
128
|
-
return node.type === "ArrayExpression" || node.type === "ArrayPattern";
|
128
|
+
return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
|
129
129
|
}
|
130
130
|
|
131
131
|
/**
|
@@ -0,0 +1,71 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to require braces in arrow function body.
|
3
|
+
* @author Alberto Rodríguez
|
4
|
+
* @copyright 2015 Alberto Rodríguez. All rights reserved.
|
5
|
+
* See LICENSE file in root directory for full license.
|
6
|
+
*/
|
7
|
+
"use strict";
|
8
|
+
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Rule Definition
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
module.exports = function(context) {
|
14
|
+
var always = context.options[0] === "always";
|
15
|
+
var asNeeded = !context.options[0] || context.options[0] === "as-needed";
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Determines whether a arrow function body needs braces
|
19
|
+
* @param {ASTNode} node The arrow function node.
|
20
|
+
* @returns {void}
|
21
|
+
*/
|
22
|
+
function validate(node) {
|
23
|
+
var arrowBody = node.body;
|
24
|
+
if (arrowBody.type === "BlockStatement") {
|
25
|
+
var blockBody = arrowBody.body;
|
26
|
+
|
27
|
+
if (blockBody.length > 1) {
|
28
|
+
return;
|
29
|
+
}
|
30
|
+
|
31
|
+
if (blockBody.length === 0) {
|
32
|
+
var hasComments = context.getComments(arrowBody).trailing.length > 0;
|
33
|
+
if (hasComments) {
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
|
37
|
+
context.report({
|
38
|
+
node: node,
|
39
|
+
loc: arrowBody.loc.start,
|
40
|
+
message: "Unexpected empty block in arrow body."
|
41
|
+
});
|
42
|
+
} else {
|
43
|
+
if (asNeeded && blockBody[0].type === "ReturnStatement") {
|
44
|
+
context.report({
|
45
|
+
node: node,
|
46
|
+
loc: arrowBody.loc.start,
|
47
|
+
message: "Unexpected block statement surrounding arrow body."
|
48
|
+
});
|
49
|
+
}
|
50
|
+
}
|
51
|
+
} else {
|
52
|
+
if (always) {
|
53
|
+
context.report({
|
54
|
+
node: node,
|
55
|
+
loc: arrowBody.loc.start,
|
56
|
+
message: "Expected block statement surrounding arrow body."
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
return {
|
63
|
+
"ArrowFunctionExpression": validate
|
64
|
+
};
|
65
|
+
};
|
66
|
+
|
67
|
+
module.exports.schema = [
|
68
|
+
{
|
69
|
+
"enum": ["always", "as-needed"]
|
70
|
+
}
|
71
|
+
];
|
@@ -16,12 +16,12 @@
|
|
16
16
|
/**
|
17
17
|
* Gets the last element of a given array.
|
18
18
|
*
|
19
|
-
* @param {
|
20
|
-
* @returns {
|
19
|
+
* @param {*[]} xs - An array to get.
|
20
|
+
* @returns {*} The last element, or undefined.
|
21
21
|
*/
|
22
22
|
function getLast(xs) {
|
23
23
|
if (xs.length === 0) {
|
24
|
-
return
|
24
|
+
return null;
|
25
25
|
}
|
26
26
|
return xs[xs.length - 1];
|
27
27
|
}
|
@@ -79,9 +79,9 @@ module.exports = function(context) {
|
|
79
79
|
return false;
|
80
80
|
}
|
81
81
|
|
82
|
-
var sourceCode = context.getSourceCode()
|
83
|
-
|
84
|
-
|
82
|
+
var sourceCode = context.getSourceCode(),
|
83
|
+
penultimateToken = sourceCode.getLastToken(lastItem),
|
84
|
+
lastToken = sourceCode.getLastToken(node);
|
85
85
|
|
86
86
|
if (lastToken.value === ",") {
|
87
87
|
penultimateToken = lastToken;
|
@@ -105,8 +105,16 @@ module.exports = function(context) {
|
|
105
105
|
return;
|
106
106
|
}
|
107
107
|
|
108
|
-
var sourceCode = context.getSourceCode()
|
109
|
-
|
108
|
+
var sourceCode = context.getSourceCode(),
|
109
|
+
trailingToken;
|
110
|
+
|
111
|
+
// last item can be surrounded by parentheses for object and array literals
|
112
|
+
if (node.type === "ObjectExpression" || node.type === "ArrayExpression") {
|
113
|
+
trailingToken = sourceCode.getTokenBefore(sourceCode.getLastToken(node));
|
114
|
+
} else {
|
115
|
+
trailingToken = sourceCode.getTokenAfter(lastItem);
|
116
|
+
}
|
117
|
+
|
110
118
|
if (trailingToken.value === ",") {
|
111
119
|
context.report(
|
112
120
|
lastItem,
|
@@ -137,8 +145,16 @@ module.exports = function(context) {
|
|
137
145
|
return;
|
138
146
|
}
|
139
147
|
|
140
|
-
var sourceCode = context.getSourceCode()
|
141
|
-
|
148
|
+
var sourceCode = context.getSourceCode(),
|
149
|
+
trailingToken;
|
150
|
+
|
151
|
+
// last item can be surrounded by parentheses for object and array literals
|
152
|
+
if (node.type === "ObjectExpression" || node.type === "ArrayExpression") {
|
153
|
+
trailingToken = sourceCode.getTokenBefore(sourceCode.getLastToken(node));
|
154
|
+
} else {
|
155
|
+
trailingToken = sourceCode.getTokenAfter(lastItem);
|
156
|
+
}
|
157
|
+
|
142
158
|
if (trailingToken.value !== ",") {
|
143
159
|
context.report(
|
144
160
|
lastItem,
|