eslint 3.16.1 → 3.19.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1 -0
  3. package/conf/eslint-recommended.js +2 -0
  4. package/lib/ast-utils.js +3 -67
  5. package/lib/code-path-analysis/code-path-analyzer.js +2 -7
  6. package/lib/code-path-analysis/debug-helpers.js +17 -16
  7. package/lib/config/config-file.js +68 -38
  8. package/lib/config/config-rule.js +14 -10
  9. package/lib/config/plugins.js +19 -8
  10. package/lib/eslint.js +11 -10
  11. package/lib/formatters/codeframe.js +4 -9
  12. package/lib/formatters/stylish.js +5 -4
  13. package/lib/ignored-paths.js +6 -0
  14. package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
  15. package/lib/rules/array-callback-return.js +15 -5
  16. package/lib/rules/arrow-body-style.js +2 -2
  17. package/lib/rules/arrow-parens.js +9 -3
  18. package/lib/rules/capitalized-comments.js +2 -1
  19. package/lib/rules/comma-dangle.js +3 -2
  20. package/lib/rules/comma-spacing.js +4 -14
  21. package/lib/rules/comma-style.js +8 -14
  22. package/lib/rules/complexity.js +14 -8
  23. package/lib/rules/consistent-return.js +17 -10
  24. package/lib/rules/curly.js +2 -2
  25. package/lib/rules/dot-notation.js +12 -6
  26. package/lib/rules/func-name-matching.js +18 -7
  27. package/lib/rules/func-names.js +20 -5
  28. package/lib/rules/keyword-spacing.js +19 -4
  29. package/lib/rules/line-comment-position.js +15 -5
  30. package/lib/rules/lines-around-comment.js +19 -0
  31. package/lib/rules/lines-around-directive.js +1 -1
  32. package/lib/rules/max-params.js +17 -4
  33. package/lib/rules/max-statements.js +11 -10
  34. package/lib/rules/new-parens.js +7 -21
  35. package/lib/rules/no-compare-neg-zero.js +53 -0
  36. package/lib/rules/no-cond-assign.js +4 -17
  37. package/lib/rules/no-else-return.js +19 -4
  38. package/lib/rules/no-empty-function.js +9 -16
  39. package/lib/rules/no-extra-parens.js +110 -121
  40. package/lib/rules/no-extra-semi.js +16 -3
  41. package/lib/rules/no-global-assign.js +1 -1
  42. package/lib/rules/no-implicit-coercion.js +21 -8
  43. package/lib/rules/no-invalid-regexp.js +2 -1
  44. package/lib/rules/no-multiple-empty-lines.js +2 -4
  45. package/lib/rules/no-native-reassign.js +1 -1
  46. package/lib/rules/no-negated-in-lhs.js +1 -1
  47. package/lib/rules/no-new-func.js +6 -8
  48. package/lib/rules/no-new.js +2 -6
  49. package/lib/rules/no-param-reassign.js +37 -7
  50. package/lib/rules/no-process-exit.js +2 -10
  51. package/lib/rules/no-restricted-properties.js +2 -0
  52. package/lib/rules/no-restricted-syntax.js +32 -21
  53. package/lib/rules/no-return-await.js +1 -1
  54. package/lib/rules/no-sequences.js +2 -2
  55. package/lib/rules/no-sync.js +8 -13
  56. package/lib/rules/no-unsafe-negation.js +1 -1
  57. package/lib/rules/no-unused-expressions.js +10 -1
  58. package/lib/rules/no-unused-vars.js +12 -12
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +12 -1
  61. package/lib/rules/no-useless-escape.js +8 -2
  62. package/lib/rules/no-useless-return.js +13 -2
  63. package/lib/rules/nonblock-statement-body-position.js +114 -0
  64. package/lib/rules/object-curly-spacing.js +2 -2
  65. package/lib/rules/object-shorthand.js +10 -3
  66. package/lib/rules/operator-assignment.js +20 -3
  67. package/lib/rules/padded-blocks.js +37 -31
  68. package/lib/rules/prefer-const.js +1 -1
  69. package/lib/rules/prefer-destructuring.js +1 -1
  70. package/lib/rules/quotes.js +1 -0
  71. package/lib/rules/semi-spacing.js +2 -15
  72. package/lib/rules/semi.js +17 -13
  73. package/lib/rules/sort-vars.js +3 -5
  74. package/lib/rules/space-before-function-paren.js +53 -77
  75. package/lib/rules/space-in-parens.js +4 -8
  76. package/lib/rules/space-unary-ops.js +19 -1
  77. package/lib/rules/strict.js +8 -2
  78. package/lib/rules/yoda.js +2 -2
  79. package/lib/testers/rule-tester.js +44 -13
  80. package/lib/util/fix-tracker.js +121 -0
  81. package/lib/util/glob-util.js +1 -1
  82. package/lib/util/node-event-generator.js +274 -4
  83. package/lib/util/source-code-fixer.js +3 -9
  84. package/lib/util/source-code.js +99 -2
  85. package/lib/util/traverser.js +16 -25
  86. package/package.json +34 -34
package/CHANGELOG.md CHANGED
@@ -1,3 +1,106 @@
1
+ v3.19.0 - March 31, 2017
2
+
3
+ * e09132f Fix: no-extra-parens false positive with exports and object literals (#8359) (Teddy Katz)
4
+ * 91baed4 Update: allow custom messages in no-restricted-syntax (fixes #8298) (#8357) (Vitor Balocco)
5
+ * 35c93e6 Fix: prevent space-before-function-paren from checking type annotations (#8349) (Teddy Katz)
6
+ * 3342e9f Fix: don't modify operator precedence in operator-assignment autofixer (#8358) (Teddy Katz)
7
+ * f88375f Docs: clarify that no-unsafe-negation is in eslint:recommended (#8371) (Teddy Katz)
8
+ * 02f0d27 Docs: Add soda0289 to Development Team (#8367) (Kai Cataldo)
9
+ * 155424c Fix: ignore empty path in patterns (fixes #8362) (#8364) (alberto)
10
+ * 27616a8 Fix: prefer-const false positive with object spread (fixes #8187) (#8297) (Vitor Balocco)
11
+ * 8569a90 Docs: add note about git's linebreak handling to linebreak-style docs (#8361) (Teddy Katz)
12
+ * 5878593 Chore: fix invalid syntax in no-param-reassign test (#8360) (Teddy Katz)
13
+ * 1b1046b Fix: don't classify plugins that throw errors as "missing" (fixes #6874) (#8323) (Teddy Katz)
14
+ * 29f4ba5 Fix: no-useless-computed-key invalid autofix for getters and setters (#8335) (Teddy Katz)
15
+ * 0541eaf Fix: no-implicit-coercion invalid autofix with consecutive identifiers (#8340) (Teddy Katz)
16
+ * 41b9786 Fix: no-extra-parens false positive with objects following arrows (#8339) (Teddy Katz)
17
+ * 3146167 Fix: `eslint.verify` should not mutate config argument (fixes #8329) (#8334) (alberto)
18
+ * 927de90 Fix: dot-notation autofix produces invalid syntax for integer properties (#8332) (Teddy Katz)
19
+ * a9d1bea Fix: comma-style autofix produces errors on parenthesized elements (#8331) (Teddy Katz)
20
+ * d52173f Fix: don't generate invalid options in config-rule (#8326) (Teddy Katz)
21
+ * 6eda3b5 Fix: no-extra-parens invalid autofix in for-of statements (#8337) (Teddy Katz)
22
+ * 6c819d8 Fix: dot-notation autofix produces errors on parenthesized computed keys (#8330) (Teddy Katz)
23
+ * 2d883d7 Fix: object-shorthand autofix produces errors on parenthesized functions (#8328) (Teddy Katz)
24
+ * cd9b774 Fix: quotes false positive with backtick option in method names (#8327) (Teddy Katz)
25
+ * d064ba2 Fix: no-else-return false positive for ifs in single-statement position (#8338) (Teddy Katz)
26
+ * 6a718ba Chore: enable max-statements-per-line on ESLint codebase (#8321) (Teddy Katz)
27
+ * 614b62e Chore: update sinon calls to deprecated API. (#8310) (alberto)
28
+ * 0491572 Chore: use precalculated counts in codeframe formatter (#8296) (Vitor Balocco)
29
+ * 8733e6a Chore: Fix incorrect error location properties in tests (#8307) (alberto)
30
+ * c4ffb49 Chore: Fix typos in test option assertions (#8305) (Teddy Katz)
31
+ * 79a97cb Upgrade: devDependencies (#8303) (alberto)
32
+ * e4da200 Upgrade: Mocha to 3.2.0 (#8299) (Ilya Volodin)
33
+ * 2f144ca Fix: operator-assignment autofix errors with parentheses (fixes #8293) (#8294) (Teddy Katz)
34
+ * 7521cd5 Chore: update token logic in rules to use ast-utils (#8288) (Teddy Katz)
35
+ * 9b509ce Chore: refactor space-before-function-paren rule (#8284) (Teddy Katz)
36
+ * ddc6350 Fix: no-param-reassign false positive on destructuring (fixes #8279) (#8281) (Teddy Katz)
37
+ * f8176b3 Chore: improve test coverage for node-event-generator (#8287) (Teddy Katz)
38
+ * 602e9c2 Docs: fix incorrect selector examples (#8278) (Teddy Katz)
39
+
40
+ v3.18.0 - March 17, 2017
41
+
42
+ * 85f74ca Fix: broken code path of direct nested loops (fixes #8248) (#8274) (Toru Nagashima)
43
+ * a61c359 Fix: Ignore hidden folders when resolving globs (fixes #8259) (#8270) (Ian VanSchooten)
44
+ * 6f05546 Chore: convert StubModuleResolver in config tests to ES6 class (#8265) (Teddy Katz)
45
+ * 0c0fc31 Fix: false positive of no-extra-parens about spread and sequense (#8275) (Toru Nagashima)
46
+ * e104973 Docs: remove self-reference in no-restricted-syntax docs (#8277) (Vitor Balocco)
47
+ * 23eca51 Update: Add allowTaggedTemplates to no-unused-expressions (fixes #7632) (#8253) (Kevin Partington)
48
+ * f9ede3f Upgrade: doctrine to 2.0.0 (#8269) (alberto)
49
+ * 1b678a6 New: allow rules to listen for AST selectors (fixes #5407) (#7833) (Teddy Katz)
50
+ * 63ca0c5 Chore: use precalculated counts in stylish formatter (#8251) (alberto)
51
+ * 47c3171 Fix: typo in console.error (#8258) (Jan Peer Stöcklmair)
52
+ * e74ed6d Chore: convert Traverser to ES6 class (refs #7849) (#8232) (Teddy Katz)
53
+ * 13eead9 Fix: sort-vars crash on mixed destructuring declarations (#8245) (Teddy Katz)
54
+ * 133f489 Fix: func-name-matching crash on destructuring assignment to functions (#8247) (Teddy Katz)
55
+ * a34b9c4 Fix: func-name-matching crash on non-string literal computed keys (#8246) (Teddy Katz)
56
+ * 7276e6d Docs: remove unneeded semicolons in arrow-parens.md (#8249) (Dmitry Gershun)
57
+ * 8c40a25 concat-stream known to be vulnerable prior 1.5.2 (#8228) (Samuel)
58
+ * 149c055 Upgrade: mock-fs to v4.2.0 (fixes #8194) (#8243) (Teddy Katz)
59
+ * a83bff9 Build: remove unneeded json config in demo (fixes #8237) (#8242) (alberto)
60
+ * df12137 Docs: fix typos (#8235) (Gyandeep Singh)
61
+ * b5e9788 Chore: rename no-extra-parens methods (#8225) (Vitor Balocco)
62
+ * 7f8afe6 Update: no-extra-parens overlooked spread and superClass (fixes #8175) (#8209) (Toru Nagashima)
63
+ * ce6ff56 Docs: set recommended true for no-global-assign (fixes #8215) (#8218) (BinYi LIU)
64
+ * 5b5c236 Fix: wrong comment when module not found in config (fixes #8192) (#8196) (alberto)
65
+
66
+ v3.17.1 - March 6, 2017
67
+
68
+ * f8c8e6e Build: change mock-fs path without SSH (fixes #8207) (#8208) (Toru Nagashima)
69
+ * f713f11 Fix: nonblock-statement-body-position multiline error (fixes #8202) (#8203) (Teddy Katz)
70
+ * 41e3d9c Fix: `operator-assignment` with parenthesized expression (fixes #8190) (#8197) (alberto)
71
+ * 5e3bca7 Chore: add eslint-plugin-eslint-plugin (#8198) (Teddy Katz)
72
+ * 580da36 Chore: add missing `output` property to tests (#8195) (alberto)
73
+
74
+ v3.17.0 - March 3, 2017
75
+
76
+ * 4fdf6d7 Update: deprecate `applyDefaultPatterns` in `line-comment-position` (#8183) (alberto)
77
+ * 25e5817 Fix: Don't autofix `+ +a` to `++a` in space-unary-ops (#8176) (Alan Pierce)
78
+ * a6ce8f9 Build: Sort rules before dumping them to doc files (#8154) (Danny Andrews)
79
+ * 0af9057 Chore: Upgrade to a patched version of mock-fs (fixes #8177) (#8188) (Teddy Katz)
80
+ * bf4d8cf Update: ignore eslint comments in lines-arount-comment (fixes #4345) (#8155) (alberto)
81
+ * dad20ad New: add SourceCode#getLocFromIndex and #getIndexFromLoc (fixes #8073) (#8158) (Teddy Katz)
82
+ * 18a519f Update: let RuleTester cases assert that no autofix occurs (fixes #8157) (#8163) (Teddy Katz)
83
+ * a30eb8d Docs: improve documentation for RuleTester cases (#8162) (Teddy Katz)
84
+ * a78ec9f Chore: upgrade `coveralls` to ^2.11.16 (#8161) (alberto)
85
+ * d02bd11 Fix: padded-blocks autofix problems with comments (#8149) (alberto)
86
+ * 9994889 Docs: Add missing space to `create` in `no-use-before-define` (#8166) (Justin Anastos)
87
+ * 4d542ba Docs: Remove unneeded statement about autofix (#8164) (alberto)
88
+ * 20daea5 New: no-compare-neg-zero rule (#8091) (薛定谔的猫)
89
+ * 4d35a81 Fix: Add a utility to avoid autofix conflicts (fixes #7928, fixes #8026) (#8067) (Alan Pierce)
90
+ * 287e882 New: nonblock-statement-body-position rule (fixes #6067) (#8108) (Teddy Katz)
91
+ * 7f1f4e5 Chore: remove unneeded devDeps `linefix` and `gh-got` (#8160) (alberto)
92
+ * ca1694b Update: ignore negative ranges in fixes (#8133) (alberto)
93
+ * 163d751 Docs: `lines-around-comment` doesn't disallow empty lines (#8151) (alberto)
94
+ * 1c84922 Chore: upgrade eslint-plugin-node (#8156) (alberto)
95
+ * 1ee5c27 Fix: Make RuleTester handle empty-string cases gracefully (fixes #8142) (#8143) (Teddy Katz)
96
+ * 044bc10 Docs: Add details about "--fix" option for "sort-imports" rule (#8077) (Olivier Audard)
97
+ * 3fec54a Add option to ignore property in no-param-reassign (#8087) (Christian Bundy)
98
+ * 4e52cfc Fix: Improve keyword-spacing typescript support (fixes #8110) (#8111) (Reyad Attiyat)
99
+ * 7ff42e8 New: Allow regexes in RuleTester (fixes #7837) (#8115) (Daniel Lo Nigro)
100
+ * cbd7ded Build: display rules’ meta data in their docs (fixes #5774) (#8127) (Wilson Kurniawan)
101
+ * da8e8af Update: include function name in report message if possible (fixes #7260) (#8058) (Dieter Luypaert)
102
+ * 8f91e32 Fix: `ignoreRestSiblings` option didn't cover arguments (fixes #8119) (#8120) (Toru Nagashima)
103
+
1
104
  v3.16.1 - February 22, 2017
2
105
 
3
106
  * ff8a80c Fix: duplicated autofix output for inverted fix ranges (fixes #8116) (#8117) (Teddy Katz)
package/README.md CHANGED
@@ -130,6 +130,7 @@ These folks keep the project moving and are resources for help.
130
130
  * Kevin Partington ([@platinumazure](https://github.com/platinumazure))
131
131
  * Vitor Balocco ([@vitorbal](https://github.com/vitorbal))
132
132
  * James Henry ([@JamesHenry](https://github.com/JamesHenry))
133
+ * Reyad Attiyat ([@soda0289](https://github.com/soda0289))
133
134
 
134
135
  ## Releases
135
136
 
@@ -84,6 +84,7 @@ module.exports = {
84
84
  "no-case-declarations": "error",
85
85
  "no-catch-shadow": "off",
86
86
  "no-class-assign": "error",
87
+ "no-compare-neg-zero": "off",
87
88
  "no-cond-assign": "error",
88
89
  "no-confusing-arrow": "off",
89
90
  "no-console": "error",
@@ -209,6 +210,7 @@ module.exports = {
209
210
  "no-warning-comments": "off",
210
211
  "no-whitespace-before-property": "off",
211
212
  "no-with": "off",
213
+ "nonblock-statement-body-position": "off",
212
214
  "object-curly-newline": "off",
213
215
  "object-curly-spacing": ["off", "never"],
214
216
  "object-property-newline": "off",
package/lib/ast-utils.js CHANGED
@@ -10,7 +10,6 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const esutils = require("esutils");
13
- const lodash = require("lodash");
14
13
 
15
14
  //------------------------------------------------------------------------------
16
15
  // Helpers
@@ -24,6 +23,8 @@ const bindOrCallOrApplyPattern = /^(?:bind|call|apply)$/;
24
23
  const breakableTypePattern = /^(?:(?:Do)?While|For(?:In|Of)?|Switch)Statement$/;
25
24
  const thisTagPattern = /^[\s*]*@this/m;
26
25
 
26
+
27
+ const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/;
27
28
  const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
28
29
  const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/;
29
30
 
@@ -403,45 +404,12 @@ function createGlobalLinebreakMatcher() {
403
404
  return new RegExp(LINEBREAK_MATCHER.source, "g");
404
405
  }
405
406
 
406
- const lineIndexCache = new WeakMap();
407
-
408
- /**
409
- * Gets the range index for the first character in each of the lines of `sourceCode`.
410
- * @param {SourceCode} sourceCode A sourceCode object
411
- * @returns {number[]} The indices of the first characters in the each of the lines of the code
412
- */
413
- function getLineIndices(sourceCode) {
414
-
415
- if (!lineIndexCache.has(sourceCode)) {
416
- const lineIndices = [0];
417
- const lineEndingPattern = createGlobalLinebreakMatcher();
418
- let match;
419
-
420
- /*
421
- * Previously, this function was implemented using a regex that
422
- * matched a sequence of non-linebreak characters followed by a
423
- * linebreak, then adding the lengths of the matches. However,
424
- * this caused a catastrophic backtracking issue when the end
425
- * of a file contained a large number of non-newline characters.
426
- * To avoid this, the current implementation just matches newlines
427
- * and uses match.index to get the correct line start indices.
428
- */
429
-
430
- while ((match = lineEndingPattern.exec(sourceCode.text))) {
431
- lineIndices.push(match.index + match[0].length);
432
- }
433
-
434
- // Store the sourceCode object in a WeakMap to avoid iterating over all of the lines every time a sourceCode object is passed in.
435
- lineIndexCache.set(sourceCode, lineIndices);
436
- }
437
- return lineIndexCache.get(sourceCode);
438
- }
439
-
440
407
  //------------------------------------------------------------------------------
441
408
  // Public Interface
442
409
  //------------------------------------------------------------------------------
443
410
 
444
411
  module.exports = {
412
+ COMMENTS_IGNORE_PATTERN,
445
413
  LINEBREAKS,
446
414
  LINEBREAK_MATCHER,
447
415
  STATEMENT_LIST_PARENTS,
@@ -1207,38 +1175,6 @@ module.exports = {
1207
1175
  };
1208
1176
  },
1209
1177
 
1210
- /*
1211
- * Converts a range index into a (line, column) pair.
1212
- * @param {SourceCode} sourceCode A SourceCode object
1213
- * @param {number} rangeIndex The range index of a character in a file
1214
- * @returns {Object} A {line, column} location object with a 0-indexed column
1215
- */
1216
- getLocationFromRangeIndex(sourceCode, rangeIndex) {
1217
- const lineIndices = getLineIndices(sourceCode);
1218
-
1219
- /*
1220
- * lineIndices is a sorted list of indices of the first character of each line.
1221
- * To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
1222
- * be inserted into lineIndices to keep the list sorted.
1223
- */
1224
- const lineNumber = lodash.sortedLastIndex(lineIndices, rangeIndex);
1225
-
1226
- return { line: lineNumber, column: rangeIndex - lineIndices[lineNumber - 1] };
1227
-
1228
- },
1229
-
1230
- /**
1231
- * Converts a (line, column) pair into a range index.
1232
- * @param {SourceCode} sourceCode A SourceCode object
1233
- * @param {Object} loc A line/column location
1234
- * @param {number} loc.line The line number of the location (1-indexed)
1235
- * @param {number} loc.column The column number of the location (0-indexed)
1236
- * @returns {number} The range index of the location in the file.
1237
- */
1238
- getRangeIndexFromLocation(sourceCode, loc) {
1239
- return getLineIndices(sourceCode)[loc.line - 1] + loc.column;
1240
- },
1241
-
1242
1178
  /**
1243
1179
  * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses
1244
1180
  * surrounding the node.
@@ -512,13 +512,8 @@ function processCodePathToExit(analyzer, node) {
512
512
  break;
513
513
  }
514
514
 
515
- /*
516
- * Skip updating the current segment to avoid creating useless segments if
517
- * the node type is the same as the parent node type.
518
- */
519
- if (!dontForward && (!node.parent || node.type !== node.parent.type)) {
520
-
521
- // Emits onCodePathSegmentStart events if updated.
515
+ // Emits onCodePathSegmentStart events if updated.
516
+ if (!dontForward) {
522
517
  forwardCurrentToHead(analyzer, node);
523
518
  }
524
519
  debug.dumpState(node, state, true);
@@ -107,22 +107,23 @@ module.exports = {
107
107
  text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
108
108
  }
109
109
 
110
- if (segment.internal.nodes.length > 0) {
111
- text += segment.internal.nodes.map(node => {
112
- switch (node.type) {
113
- case "Identifier": return `${node.type} (${node.name})`;
114
- case "Literal": return `${node.type} (${node.value})`;
115
- default: return node.type;
116
- }
117
- }).join("\\n");
118
- } else if (segment.internal.exitNodes.length > 0) {
119
- text += segment.internal.exitNodes.map(node => {
120
- switch (node.type) {
121
- case "Identifier": return `${node.type}:exit (${node.name})`;
122
- case "Literal": return `${node.type}:exit (${node.value})`;
123
- default: return `${node.type}:exit`;
124
- }
125
- }).join("\\n");
110
+ if (segment.internal.nodes.length > 0 || segment.internal.exitNodes.length > 0) {
111
+ text += [].concat(
112
+ segment.internal.nodes.map(node => {
113
+ switch (node.type) {
114
+ case "Identifier": return `${node.type} (${node.name})`;
115
+ case "Literal": return `${node.type} (${node.value})`;
116
+ default: return node.type;
117
+ }
118
+ }),
119
+ segment.internal.exitNodes.map(node => {
120
+ switch (node.type) {
121
+ case "Identifier": return `${node.type}:exit (${node.name})`;
122
+ case "Literal": return `${node.type}:exit (${node.value})`;
123
+ default: return `${node.type}:exit`;
124
+ }
125
+ })
126
+ ).join("\\n");
126
127
  } else {
127
128
  text += "????";
128
129
  }
@@ -183,6 +183,22 @@ function loadPackageJSONConfigFile(filePath) {
183
183
  }
184
184
  }
185
185
 
186
+ /**
187
+ * Creates an error to notify about a missing config to extend from.
188
+ * @param {string} configName The name of the missing config.
189
+ * @returns {Error} The error object to throw
190
+ * @private
191
+ */
192
+ function configMissingError(configName) {
193
+ const error = new Error(`Failed to load config "${configName}" to extend from.`);
194
+
195
+ error.messageTemplate = "extend-config-missing";
196
+ error.messageData = {
197
+ configName
198
+ };
199
+ return error;
200
+ }
201
+
186
202
  /**
187
203
  * Loads a configuration file regardless of the source. Inspects the file path
188
204
  * to determine the correctly way to load the config file.
@@ -199,6 +215,9 @@ function loadConfigFile(file) {
199
215
  config = loadJSConfigFile(filePath);
200
216
  if (file.configName) {
201
217
  config = config.configs[file.configName];
218
+ if (!config) {
219
+ throw configMissingError(file.configFullName);
220
+ }
202
221
  }
203
222
  break;
204
223
 
@@ -340,6 +359,33 @@ function getLookupPath(configFilePath) {
340
359
  return path.join(basedir, "node_modules");
341
360
  }
342
361
 
362
+ /**
363
+ * Resolves a eslint core config path
364
+ * @param {string} name The eslint config name.
365
+ * @returns {string} The resolved path of the config.
366
+ * @private
367
+ */
368
+ function getEslintCoreConfigPath(name) {
369
+ if (name === "eslint:recommended") {
370
+
371
+ /*
372
+ * Add an explicit substitution for eslint:recommended to
373
+ * conf/eslint-recommended.js.
374
+ */
375
+ return path.resolve(__dirname, "../../conf/eslint-recommended.js");
376
+ }
377
+
378
+ if (name === "eslint:all") {
379
+
380
+ /*
381
+ * Add an explicit substitution for eslint:all to conf/eslint-all.js
382
+ */
383
+ return path.resolve(__dirname, "../../conf/eslint-all.js");
384
+ }
385
+
386
+ throw configMissingError(name);
387
+ }
388
+
343
389
  /**
344
390
  * Applies values from the "extends" field in a configuration file.
345
391
  * @param {Object} config The configuration information.
@@ -360,43 +406,23 @@ function applyExtends(config, filePath, relativeTo) {
360
406
 
361
407
  // Make the last element in an array take the highest precedence
362
408
  config = configExtends.reduceRight((previousValue, parentPath) => {
363
-
364
- if (parentPath === "eslint:recommended") {
365
-
366
- /*
367
- * Add an explicit substitution for eslint:recommended to
368
- * conf/eslint-recommended.js.
369
- */
370
- parentPath = path.resolve(__dirname, "../../conf/eslint-recommended.js");
371
- } else if (parentPath === "eslint:all") {
372
-
373
- /*
374
- * Add an explicit substitution for eslint:all to conf/eslint-all.js
375
- */
376
- parentPath = path.resolve(__dirname, "../../conf/eslint-all.js");
377
- } else if (isFilePath(parentPath)) {
378
-
379
- /*
380
- * If the `extends` path is relative, use the directory of the current configuration
381
- * file as the reference point. Otherwise, use as-is.
382
- */
383
- parentPath = (!path.isAbsolute(parentPath)
384
- ? path.join(relativeTo || path.dirname(filePath), parentPath)
385
- : parentPath
386
- );
387
- }
388
-
389
409
  try {
410
+ if (parentPath.startsWith("eslint:")) {
411
+ parentPath = getEslintCoreConfigPath(parentPath);
412
+ } else if (isFilePath(parentPath)) {
413
+
414
+ /*
415
+ * If the `extends` path is relative, use the directory of the current configuration
416
+ * file as the reference point. Otherwise, use as-is.
417
+ */
418
+ parentPath = (path.isAbsolute(parentPath)
419
+ ? parentPath
420
+ : path.join(relativeTo || path.dirname(filePath), parentPath)
421
+ );
422
+ }
390
423
  debug(`Loading ${parentPath}`);
391
424
  return ConfigOps.merge(load(parentPath, false, relativeTo), previousValue);
392
425
  } catch (e) {
393
- if (parentPath.indexOf("plugin:") === 0 || parentPath.indexOf("eslint:") === 0) {
394
- e.message = `Failed to load config "${parentPath}" to extend from.`;
395
- e.messageTemplate = "extend-config-missing";
396
- e.messageData = {
397
- configName: parentPath
398
- };
399
- }
400
426
 
401
427
  /*
402
428
  * If the file referenced by `extends` failed to load, add the path
@@ -462,7 +488,10 @@ function normalizePackageName(name, prefix) {
462
488
  * or package name.
463
489
  * @param {string} filePath The filepath to resolve.
464
490
  * @param {string} [relativeTo] The path to resolve relative to.
465
- * @returns {Object} A path that can be used directly to load the configuration.
491
+ * @returns {Object} An object containing 3 properties:
492
+ * - 'filePath' (required) the resolved path that can be used directly to load the configuration.
493
+ * - 'configName' the name of the configuration inside the plugin.
494
+ * - 'configFullName' the name of the configuration as used in the eslint config (e.g. 'plugin:node/recommended').
466
495
  * @private
467
496
  */
468
497
  function resolve(filePath, relativeTo) {
@@ -471,14 +500,15 @@ function resolve(filePath, relativeTo) {
471
500
  }
472
501
  let normalizedPackageName;
473
502
 
474
- if (filePath.indexOf("plugin:") === 0) {
475
- const packagePath = filePath.substr(7, filePath.lastIndexOf("/") - 7);
503
+ if (filePath.startsWith("plugin:")) {
504
+ const configFullName = filePath;
505
+ const pluginName = filePath.substr(7, filePath.lastIndexOf("/") - 7);
476
506
  const configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1);
477
507
 
478
- normalizedPackageName = normalizePackageName(packagePath, "eslint-plugin");
508
+ normalizedPackageName = normalizePackageName(pluginName, "eslint-plugin");
479
509
  debug(`Attempting to resolve ${normalizedPackageName}`);
480
510
  filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
481
- return { filePath, configName };
511
+ return { filePath, configName, configFullName };
482
512
  }
483
513
  normalizedPackageName = normalizePackageName(filePath, "eslint-config");
484
514
  debug(`Attempting to resolve ${normalizedPackageName}`);
@@ -223,7 +223,7 @@ class RuleConfigSet {
223
223
  /**
224
224
  * Add rule configurations from a schema object
225
225
  * @param {Object} obj Schema item with type === "object"
226
- * @returns {void}
226
+ * @returns {boolean} true if at least one schema for the object could be generated, false otherwise
227
227
  */
228
228
  addObject(obj) {
229
229
  const objectConfigSet = {
@@ -258,7 +258,10 @@ class RuleConfigSet {
258
258
 
259
259
  if (objectConfigSet.objectConfigs.length > 0) {
260
260
  this.ruleConfigs = this.ruleConfigs.concat(combineArrays(this.ruleConfigs, objectConfigSet.objectConfigs));
261
+ return true;
261
262
  }
263
+
264
+ return false;
262
265
  }
263
266
  }
264
267
 
@@ -271,20 +274,21 @@ function generateConfigsFromSchema(schema) {
271
274
  const configSet = new RuleConfigSet();
272
275
 
273
276
  if (Array.isArray(schema)) {
274
- schema.forEach(opt => {
277
+ for (const opt of schema) {
275
278
  if (opt.enum) {
276
279
  configSet.addEnums(opt.enum);
277
- }
278
-
279
- if (opt.type && opt.type === "object") {
280
- configSet.addObject(opt);
281
- }
280
+ } else if (opt.type && opt.type === "object") {
281
+ if (!configSet.addObject(opt)) {
282
+ break;
283
+ }
282
284
 
283
- if (opt.oneOf) {
285
+ // TODO (IanVS): support oneOf
286
+ } else {
284
287
 
285
- // TODO (IanVS): not yet implemented
288
+ // If we don't know how to fill in this option, don't fill in any of the following options.
289
+ break;
286
290
  }
287
- });
291
+ }
288
292
  }
289
293
  configSet.addErrorSeverity();
290
294
  return configSet.ruleConfigs;
@@ -127,14 +127,25 @@ module.exports = {
127
127
  if (!plugins[shortName]) {
128
128
  try {
129
129
  plugin = require(longName);
130
- } catch (err) {
131
- debug(`Failed to load plugin ${longName}.`);
132
- err.message = `Failed to load plugin ${pluginName}: ${err.message}`;
133
- err.messageTemplate = "plugin-missing";
134
- err.messageData = {
135
- pluginName: longName
136
- };
137
- throw err;
130
+ } catch (pluginLoadErr) {
131
+ try {
132
+
133
+ // Check whether the plugin exists
134
+ require.resolve(longName);
135
+ } catch (missingPluginErr) {
136
+
137
+ // If the plugin can't be resolved, display the missing plugin error (usually a config or install error)
138
+ debug(`Failed to load plugin ${longName}.`);
139
+ missingPluginErr.message = `Failed to load plugin ${pluginName}: ${missingPluginErr.message}`;
140
+ missingPluginErr.messageTemplate = "plugin-missing";
141
+ missingPluginErr.messageData = {
142
+ pluginName: longName
143
+ };
144
+ throw missingPluginErr;
145
+ }
146
+
147
+ // Otherwise, the plugin exists and is throwing on module load for some reason, so print the stack trace.
148
+ throw pluginLoadErr;
138
149
  }
139
150
 
140
151
  this.define(pluginName, plugin);
package/lib/eslint.js CHANGED
@@ -778,17 +778,18 @@ module.exports = (function() {
778
778
  // search and apply "eslint-env *".
779
779
  const envInFile = findEslintEnv(text || textOrSourceCode.text);
780
780
 
781
+ config = Object.assign({}, config);
782
+
781
783
  if (envInFile) {
782
- if (!config || !config.env) {
783
- config = Object.assign({}, config || {}, { env: envInFile });
784
- } else {
785
- config = Object.assign({}, config);
784
+ if (config.env) {
786
785
  config.env = Object.assign({}, config.env, envInFile);
786
+ } else {
787
+ config.env = envInFile;
787
788
  }
788
789
  }
789
790
 
790
791
  // process initial config to make it safe to extend
791
- config = prepareConfig(config || {});
792
+ config = prepareConfig(config);
792
793
 
793
794
  // only do this for text
794
795
  if (text !== null) {
@@ -867,11 +868,11 @@ module.exports = (function() {
867
868
  const rule = ruleCreator.create ? ruleCreator.create(ruleContext)
868
869
  : ruleCreator(ruleContext);
869
870
 
870
- // add all the node types as listeners
871
- Object.keys(rule).forEach(nodeType => {
872
- api.on(nodeType, timing.enabled
873
- ? timing.time(key, rule[nodeType])
874
- : rule[nodeType]
871
+ // add all the selectors from the rule as listeners
872
+ Object.keys(rule).forEach(selector => {
873
+ api.on(selector, timing.enabled
874
+ ? timing.time(key, rule[selector])
875
+ : rule[selector]
875
876
  );
876
877
  });
877
878
  } catch (ex) {
@@ -101,15 +101,10 @@ module.exports = function(results) {
101
101
  const resultsWithMessages = results.filter(result => result.messages.length > 0);
102
102
 
103
103
  let output = resultsWithMessages.reduce((resultsOutput, result) => {
104
- const messages = result.messages.map(message => {
105
- if (message.fatal || message.severity === 2) {
106
- errors++;
107
- } else {
108
- warnings++;
109
- }
110
-
111
- return `${formatMessage(message, result)}\n\n`;
112
- });
104
+ const messages = result.messages.map(message => `${formatMessage(message, result)}\n\n`);
105
+
106
+ errors += result.errorCount;
107
+ warnings += result.warningCount;
113
108
 
114
109
  return resultsOutput.concat(messages);
115
110
  }, []).join("\n");
@@ -28,7 +28,6 @@ function pluralize(word, count) {
28
28
  module.exports = function(results) {
29
29
 
30
30
  let output = "\n",
31
- total = 0,
32
31
  errors = 0,
33
32
  warnings = 0,
34
33
  summaryColor = "yellow";
@@ -40,7 +39,9 @@ module.exports = function(results) {
40
39
  return;
41
40
  }
42
41
 
43
- total += messages.length;
42
+ errors += result.errorCount;
43
+ warnings += result.warningCount;
44
+
44
45
  output += `${chalk.underline(result.filePath)}\n`;
45
46
 
46
47
  output += `${table(
@@ -50,10 +51,8 @@ module.exports = function(results) {
50
51
  if (message.fatal || message.severity === 2) {
51
52
  messageType = chalk.red("error");
52
53
  summaryColor = "red";
53
- errors++;
54
54
  } else {
55
55
  messageType = chalk.yellow("warning");
56
- warnings++;
57
56
  }
58
57
 
59
58
  return [
@@ -74,6 +73,8 @@ module.exports = function(results) {
74
73
  ).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
75
74
  });
76
75
 
76
+ const total = errors + warnings;
77
+
77
78
  if (total > 0) {
78
79
  output += chalk[summaryColor].bold([
79
80
  "\u2716 ", total, pluralize(" problem", total),
@@ -201,6 +201,12 @@ class IgnoredPaths {
201
201
 
202
202
  const ig = ignore().add(DEFAULT_IGNORE_DIRS);
203
203
 
204
+ if (this.options.dotfiles !== true) {
205
+
206
+ // Ignore hidden folders. (This cannot be ".*", or else it's not possible to unignore hidden files)
207
+ ig.add([".*/*", "!../"]);
208
+ }
209
+
204
210
  if (this.options.ignore) {
205
211
  ig.add(this.ig.custom);
206
212
  }