eslint 4.18.0 → 4.19.1

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 (65) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +2 -2
  3. package/conf/environments.js +3 -1
  4. package/conf/eslint-recommended.js +0 -0
  5. package/lib/ast-utils.js +25 -29
  6. package/lib/cli-engine.js +29 -28
  7. package/lib/code-path-analysis/code-path-state.js +5 -5
  8. package/lib/code-path-analysis/code-path.js +11 -6
  9. package/lib/code-path-analysis/fork-context.js +10 -12
  10. package/lib/config/config-file.js +20 -11
  11. package/lib/config/config-ops.js +8 -10
  12. package/lib/config/config-rule.js +2 -3
  13. package/lib/config/plugins.js +20 -0
  14. package/lib/config.js +7 -8
  15. package/lib/file-finder.js +9 -10
  16. package/lib/ignored-paths.js +4 -4
  17. package/lib/linter.js +397 -406
  18. package/lib/load-rules.js +6 -7
  19. package/lib/rules/accessor-pairs.js +4 -4
  20. package/lib/rules/array-callback-return.js +8 -6
  21. package/lib/rules/array-element-newline.js +4 -4
  22. package/lib/rules/curly.js +11 -10
  23. package/lib/rules/generator-star-spacing.js +1 -2
  24. package/lib/rules/indent-legacy.js +7 -10
  25. package/lib/rules/indent.js +51 -29
  26. package/lib/rules/keyword-spacing.js +6 -18
  27. package/lib/rules/max-len.js +12 -5
  28. package/lib/rules/no-await-in-loop.js +1 -1
  29. package/lib/rules/no-buffer-constructor.js +1 -1
  30. package/lib/rules/no-control-regex.js +51 -72
  31. package/lib/rules/no-else-return.js +7 -6
  32. package/lib/rules/no-empty-character-class.js +1 -1
  33. package/lib/rules/no-eval.js +7 -8
  34. package/lib/rules/no-extra-parens.js +5 -4
  35. package/lib/rules/no-implicit-coercion.js +6 -9
  36. package/lib/rules/no-invalid-regexp.js +53 -36
  37. package/lib/rules/no-irregular-whitespace.js +1 -1
  38. package/lib/rules/no-loop-func.js +9 -11
  39. package/lib/rules/no-magic-numbers.js +17 -10
  40. package/lib/rules/no-return-assign.js +4 -3
  41. package/lib/rules/no-unexpected-multiline.js +1 -1
  42. package/lib/rules/no-unsafe-finally.js +7 -4
  43. package/lib/rules/no-useless-escape.js +2 -2
  44. package/lib/rules/no-useless-return.js +10 -9
  45. package/lib/rules/no-var.js +8 -8
  46. package/lib/rules/object-curly-newline.js +2 -1
  47. package/lib/rules/one-var.js +140 -97
  48. package/lib/rules/padding-line-between-statements.js +6 -4
  49. package/lib/rules/prefer-arrow-callback.js +5 -4
  50. package/lib/rules/prefer-template.js +5 -3
  51. package/lib/rules/space-unary-ops.js +1 -3
  52. package/lib/rules/spaced-comment.js +3 -7
  53. package/lib/rules/template-tag-spacing.js +0 -0
  54. package/lib/rules/valid-jsdoc.js +6 -6
  55. package/lib/rules/vars-on-top.js +7 -17
  56. package/lib/timing.js +3 -5
  57. package/lib/util/glob-util.js +11 -11
  58. package/lib/util/interpolate.js +5 -1
  59. package/lib/util/naming.js +11 -10
  60. package/lib/util/npm-util.js +4 -6
  61. package/lib/util/path-util.js +6 -8
  62. package/lib/util/source-code-util.js +23 -26
  63. package/lib/util/source-code.js +4 -3
  64. package/package.json +4 -3
  65. package/conf/default-config-options.js +0 -29
package/CHANGELOG.md CHANGED
@@ -1,3 +1,43 @@
1
+ v4.19.1 - March 21, 2018
2
+
3
+ * 3ff5d11 Fix: no-invalid-regexp not understand variable for flags (fixes #10112) (#10113) (薛定谔的猫)
4
+ * abc765c Fix: object-curly-newline minProperties w/default export (fixes #10101) (#10103) (Kevin Partington)
5
+ * 6f9e155 Docs: Update ambiguous for...in example for guard-for-in (#10114) (CJ R)
6
+ * 0360cc2 Chore: Adding debug logs on successful plugin loads (#10100) (Kevin Partington)
7
+ * a717c5d Chore: Adding log at beginning of unit tests in Makefile.js (#10102) (Kevin Partington)
8
+
9
+ v4.19.0 - March 16, 2018
10
+
11
+ * 55a1593 Update: consecutive option for one-var (fixes #4680) (#9994) (薛定谔的猫)
12
+ * 8d3814e Fix: false positive about ES2018 RegExp enhancements (fixes #9893) (#10062) (Toru Nagashima)
13
+ * 935f4e4 Docs: Clarify default ignoring of node_modules (#10092) (Matijs Brinkhuis)
14
+ * 72ed3db Docs: Wrap `Buffer()` in backticks in `no-buffer-constructor` rule description (#10084) (Stephen Edgar)
15
+ * 3aded2f Docs: Fix lodash typos, make spacing consistent (#10073) (Josh Smith)
16
+ * e33bb64 Chore: enable no-param-reassign on ESLint codebase (#10065) (Teddy Katz)
17
+ * 66a1e9a Docs: fix possible typo (#10060) (Vse Mozhet Byt)
18
+ * 2e68be6 Update: give a node at least the indentation of its parent (fixes #9995) (#10054) (Teddy Katz)
19
+ * 72ca5b3 Update: Correctly indent JSXText with trailing linebreaks (fixes #9878) (#10055) (Teddy Katz)
20
+ * 2a4c838 Docs: Update ECMAScript versions in FAQ (#10047) (alberto)
21
+
22
+ v4.18.2 - March 2, 2018
23
+
24
+ * 6b71fd0 Fix: table@4.0.2, because 4.0.3 needs "ajv": "^6.0.1" (#10022) (Mathieu Seiler)
25
+ * 3c697de Chore: fix incorrect comment about linter.verify return value (#10030) (Teddy Katz)
26
+ * 9df8653 Chore: refactor parser-loading out of linter.verify (#10028) (Teddy Katz)
27
+ * f6901d0 Fix: remove catastrophic backtracking vulnerability (fixes #10002) (#10019) (Jamie Davis)
28
+ * e4f52ce Chore: Simplify dataflow in linter.verify (#10020) (Teddy Katz)
29
+ * 33177cd Chore: make library files non-executable (#10021) (Teddy Katz)
30
+ * 558ccba Chore: refactor directive comment processing (#10007) (Teddy Katz)
31
+ * 18e15d9 Chore: avoid useless catch clauses that just rethrow errors (#10010) (Teddy Katz)
32
+ * a1c3759 Chore: refactor populating configs with defaults in linter (#10006) (Teddy Katz)
33
+ * aea07dc Fix: Make max-len ignoreStrings ignore JSXText (fixes #9954) (#9985) (Rachael Sim)
34
+
35
+ v4.18.1 - February 20, 2018
36
+
37
+ * f417506 Fix: ensure no-await-in-loop reports the correct node (fixes #9992) (#9993) (Teddy Katz)
38
+ * 3e99363 Docs: Fixed typo in key-spacing rule doc (#9987) (Jaid)
39
+ * 7c2cd70 Docs: deprecate experimentalObjectRestSpread (#9986) (Toru Nagashima)
40
+
1
41
  v4.18.0 - February 16, 2018
2
42
 
3
43
  * 70f22f3 Chore: Apply memoization to config creation within glob utils (#9944) (Kenton Jacobsen)
package/README.md CHANGED
@@ -227,9 +227,9 @@ In all cases, make sure your plugins' peerDependencies have been installed as we
227
227
 
228
228
  Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/user-guide/configuring)). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
229
229
 
230
- ### What about ECMAScript 6 support?
230
+ ### What ECMAScript versions does ESLint support?
231
231
 
232
- ESLint has full support for ECMAScript 6. By default, this support is off. You can enable ECMAScript 6 syntax and global variables through [configuration](https://eslint.org/docs/user-guide/configuring).
232
+ ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, and 2018. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
233
233
 
234
234
  ### What about experimental features?
235
235
 
@@ -15,7 +15,9 @@ const globals = require("globals");
15
15
  //------------------------------------------------------------------------------
16
16
 
17
17
  module.exports = {
18
- builtin: globals.es5,
18
+ builtin: {
19
+ globals: globals.es5
20
+ },
19
21
  browser: {
20
22
 
21
23
  /*
File without changes
package/lib/ast-utils.js CHANGED
@@ -84,11 +84,10 @@ function isES5Constructor(node) {
84
84
  * @returns {Node|null} A found function node.
85
85
  */
86
86
  function getUpperFunction(node) {
87
- while (node) {
88
- if (anyFunctionPattern.test(node.type)) {
89
- return node;
87
+ for (let currentNode = node; currentNode; currentNode = currentNode.parent) {
88
+ if (anyFunctionPattern.test(currentNode.type)) {
89
+ return currentNode;
90
90
  }
91
- node = node.parent;
92
91
  }
93
92
  return null;
94
93
  }
@@ -132,12 +131,10 @@ function isLoop(node) {
132
131
  * @returns {boolean} `true` if the node is in a loop.
133
132
  */
134
133
  function isInLoop(node) {
135
- while (node && !isFunction(node)) {
136
- if (isLoop(node)) {
134
+ for (let currentNode = node; currentNode && !isFunction(currentNode); currentNode = currentNode.parent) {
135
+ if (isLoop(currentNode)) {
137
136
  return true;
138
137
  }
139
-
140
- node = node.parent;
141
138
  }
142
139
 
143
140
  return false;
@@ -204,16 +201,14 @@ function isArrayFromMethod(node) {
204
201
  * @returns {boolean} Whether or not the node is a method which has `thisArg`.
205
202
  */
206
203
  function isMethodWhichHasThisArg(node) {
207
- while (node) {
208
- if (node.type === "Identifier") {
209
- return arrayMethodPattern.test(node.name);
210
- }
211
- if (node.type === "MemberExpression" && !node.computed) {
212
- node = node.property;
213
- continue;
204
+ for (
205
+ let currentNode = node;
206
+ currentNode.type === "MemberExpression" && !currentNode.computed;
207
+ currentNode = currentNode.property
208
+ ) {
209
+ if (currentNode.property.type === "Identifier") {
210
+ return arrayMethodPattern.test(currentNode.property.name);
214
211
  }
215
-
216
- break;
217
212
  }
218
213
 
219
214
  return false;
@@ -631,9 +626,10 @@ module.exports = {
631
626
  return false;
632
627
  }
633
628
  const isAnonymous = node.id === null;
629
+ let currentNode = node;
634
630
 
635
- while (node) {
636
- const parent = node.parent;
631
+ while (currentNode) {
632
+ const parent = currentNode.parent;
637
633
 
638
634
  switch (parent.type) {
639
635
 
@@ -643,7 +639,7 @@ module.exports = {
643
639
  */
644
640
  case "LogicalExpression":
645
641
  case "ConditionalExpression":
646
- node = parent;
642
+ currentNode = parent;
647
643
  break;
648
644
 
649
645
  /*
@@ -663,14 +659,14 @@ module.exports = {
663
659
  if (func === null || !isCallee(func)) {
664
660
  return true;
665
661
  }
666
- node = func.parent;
662
+ currentNode = func.parent;
667
663
  break;
668
664
  }
669
665
  case "ArrowFunctionExpression":
670
- if (node !== parent.body || !isCallee(parent)) {
666
+ if (currentNode !== parent.body || !isCallee(parent)) {
671
667
  return true;
672
668
  }
673
- node = parent.parent;
669
+ currentNode = parent.parent;
674
670
  break;
675
671
 
676
672
  /*
@@ -685,7 +681,7 @@ module.exports = {
685
681
  */
686
682
  case "Property":
687
683
  case "MethodDefinition":
688
- return parent.value !== node;
684
+ return parent.value !== currentNode;
689
685
 
690
686
  /*
691
687
  * e.g.
@@ -715,7 +711,7 @@ module.exports = {
715
711
  case "VariableDeclarator":
716
712
  return !(
717
713
  isAnonymous &&
718
- parent.init === node &&
714
+ parent.init === currentNode &&
719
715
  parent.id.type === "Identifier" &&
720
716
  startsWithUpperCase(parent.id.name)
721
717
  );
@@ -728,7 +724,7 @@ module.exports = {
728
724
  */
729
725
  case "MemberExpression":
730
726
  return (
731
- parent.object !== node ||
727
+ parent.object !== currentNode ||
732
728
  parent.property.type !== "Identifier" ||
733
729
  !bindOrCallOrApplyPattern.test(parent.property.name) ||
734
730
  !isCallee(parent) ||
@@ -746,21 +742,21 @@ module.exports = {
746
742
  if (isReflectApply(parent.callee)) {
747
743
  return (
748
744
  parent.arguments.length !== 3 ||
749
- parent.arguments[0] !== node ||
745
+ parent.arguments[0] !== currentNode ||
750
746
  isNullOrUndefined(parent.arguments[1])
751
747
  );
752
748
  }
753
749
  if (isArrayFromMethod(parent.callee)) {
754
750
  return (
755
751
  parent.arguments.length !== 3 ||
756
- parent.arguments[1] !== node ||
752
+ parent.arguments[1] !== currentNode ||
757
753
  isNullOrUndefined(parent.arguments[2])
758
754
  );
759
755
  }
760
756
  if (isMethodWhichHasThisArg(parent.callee)) {
761
757
  return (
762
758
  parent.arguments.length !== 2 ||
763
- parent.arguments[0] !== node ||
759
+ parent.arguments[0] !== currentNode ||
764
760
  isNullOrUndefined(parent.arguments[1])
765
761
  );
766
762
  }
package/lib/cli-engine.js CHANGED
@@ -157,8 +157,9 @@ function processText(text, configHelper, filename, fix, allowInlineConfig, repor
157
157
  fileExtension = path.extname(filename);
158
158
  }
159
159
 
160
- filename = filename || "<text>";
161
- debug(`Linting ${filename}`);
160
+ const effectiveFilename = filename || "<text>";
161
+
162
+ debug(`Linting ${effectiveFilename}`);
162
163
  const config = configHelper.getConfig(filePath);
163
164
 
164
165
  if (config.plugins) {
@@ -177,18 +178,18 @@ function processText(text, configHelper, filename, fix, allowInlineConfig, repor
177
178
  const autofixingEnabled = typeof fix !== "undefined" && (!processor || processor.supportsAutofix);
178
179
 
179
180
  const fixedResult = linter.verifyAndFix(text, config, {
180
- filename,
181
+ filename: effectiveFilename,
181
182
  allowInlineConfig,
182
183
  reportUnusedDisableDirectives,
183
184
  fix: !!autofixingEnabled && fix,
184
- preprocess: processor && (rawText => processor.preprocess(rawText, filename)),
185
- postprocess: processor && (problemLists => processor.postprocess(problemLists, filename))
185
+ preprocess: processor && (rawText => processor.preprocess(rawText, effectiveFilename)),
186
+ postprocess: processor && (problemLists => processor.postprocess(problemLists, effectiveFilename))
186
187
  });
187
188
 
188
189
  const stats = calculateStatsPerFile(fixedResult.messages);
189
190
 
190
191
  const result = {
191
- filePath: filename,
192
+ filePath: effectiveFilename,
192
193
  messages: fixedResult.messages,
193
194
  errorCount: stats.errorCount,
194
195
  warningCount: stats.warningCount,
@@ -302,10 +303,10 @@ function getCacheFile(cacheFile, cwd) {
302
303
  * make sure the path separators are normalized for the environment/os
303
304
  * keeping the trailing path separator if present
304
305
  */
305
- cacheFile = path.normalize(cacheFile);
306
+ const normalizedCacheFile = path.normalize(cacheFile);
306
307
 
307
- const resolvedCacheFile = path.resolve(cwd, cacheFile);
308
- const looksLikeADirectory = cacheFile[cacheFile.length - 1] === path.sep;
308
+ const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
309
+ const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
309
310
 
310
311
  /**
311
312
  * return the name for the cache file in case the provided parameter is a directory
@@ -368,16 +369,16 @@ class CLIEngine {
368
369
 
369
370
  /**
370
371
  * Creates a new instance of the core CLI engine.
371
- * @param {CLIEngineOptions} options The options for this instance.
372
+ * @param {CLIEngineOptions} providedOptions The options for this instance.
372
373
  * @constructor
373
374
  */
374
- constructor(options) {
375
+ constructor(providedOptions) {
375
376
 
376
- options = Object.assign(
377
+ const options = Object.assign(
377
378
  Object.create(null),
378
379
  defaultOptions,
379
380
  { cwd: process.cwd() },
380
- options
381
+ providedOptions
381
382
  );
382
383
 
383
384
  /**
@@ -605,20 +606,21 @@ class CLIEngine {
605
606
  ignoredPaths = new IgnoredPaths(options);
606
607
 
607
608
  // resolve filename based on options.cwd (for reporting, ignoredPaths also resolves)
608
- if (filename && !path.isAbsolute(filename)) {
609
- filename = path.resolve(options.cwd, filename);
610
- }
611
609
 
612
- if (filename && ignoredPaths.contains(filename)) {
610
+ const resolvedFilename = filename && !path.isAbsolute(filename)
611
+ ? path.resolve(options.cwd, filename)
612
+ : filename;
613
+
614
+ if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) {
613
615
  if (warnIgnored) {
614
- results.push(createIgnoreResult(filename, options.cwd));
616
+ results.push(createIgnoreResult(resolvedFilename, options.cwd));
615
617
  }
616
618
  } else {
617
619
  results.push(
618
620
  processText(
619
621
  text,
620
622
  configHelper,
621
- filename,
623
+ resolvedFilename,
622
624
  options.fix,
623
625
  options.allowInlineConfig,
624
626
  options.reportUnusedDisableDirectives,
@@ -672,31 +674,30 @@ class CLIEngine {
672
674
  */
673
675
  getFormatter(format) {
674
676
 
675
-
676
677
  // default is stylish
677
- format = format || "stylish";
678
+ const resolvedFormatName = format || "stylish";
678
679
 
679
680
  // only strings are valid formatters
680
- if (typeof format === "string") {
681
+ if (typeof resolvedFormatName === "string") {
681
682
 
682
683
  // replace \ with / for Windows compatibility
683
- format = format.replace(/\\/g, "/");
684
+ const normalizedFormatName = resolvedFormatName.replace(/\\/g, "/");
684
685
 
685
686
  const cwd = this.options ? this.options.cwd : process.cwd();
686
- const namespace = naming.getNamespaceFromTerm(format);
687
+ const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
687
688
 
688
689
  let formatterPath;
689
690
 
690
691
  // if there's a slash, then it's a file
691
- if (!namespace && format.indexOf("/") > -1) {
692
- formatterPath = path.resolve(cwd, format);
692
+ if (!namespace && normalizedFormatName.indexOf("/") > -1) {
693
+ formatterPath = path.resolve(cwd, normalizedFormatName);
693
694
  } else {
694
695
  try {
695
- const npmFormat = naming.normalizePackageName(format, "eslint-formatter");
696
+ const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
696
697
 
697
698
  formatterPath = resolver.resolve(npmFormat, `${cwd}/node_modules`);
698
699
  } catch (e) {
699
- formatterPath = `./formatters/${format}`;
700
+ formatterPath = `./formatters/${normalizedFormatName}`;
700
701
  }
701
702
  }
702
703
 
@@ -164,13 +164,13 @@ function removeConnection(prevSegments, nextSegments) {
164
164
  * Creates looping path.
165
165
  *
166
166
  * @param {CodePathState} state - The instance.
167
- * @param {CodePathSegment[]} fromSegments - Segments which are source.
168
- * @param {CodePathSegment[]} toSegments - Segments which are destination.
167
+ * @param {CodePathSegment[]} unflattenedFromSegments - Segments which are source.
168
+ * @param {CodePathSegment[]} unflattenedToSegments - Segments which are destination.
169
169
  * @returns {void}
170
170
  */
171
- function makeLooped(state, fromSegments, toSegments) {
172
- fromSegments = CodePathSegment.flattenUnusedSegments(fromSegments);
173
- toSegments = CodePathSegment.flattenUnusedSegments(toSegments);
171
+ function makeLooped(state, unflattenedFromSegments, unflattenedToSegments) {
172
+ const fromSegments = CodePathSegment.flattenUnusedSegments(unflattenedFromSegments);
173
+ const toSegments = CodePathSegment.flattenUnusedSegments(unflattenedToSegments);
174
174
 
175
175
  const end = Math.min(fromSegments.length, toSegments.length);
176
176
 
@@ -134,14 +134,19 @@ class CodePath {
134
134
  * @returns {void}
135
135
  */
136
136
  traverseSegments(options, callback) {
137
+ let resolvedOptions;
138
+ let resolvedCallback;
139
+
137
140
  if (typeof options === "function") {
138
- callback = options;
139
- options = null;
141
+ resolvedCallback = options;
142
+ resolvedOptions = {};
143
+ } else {
144
+ resolvedOptions = options || {};
145
+ resolvedCallback = callback;
140
146
  }
141
147
 
142
- options = options || {};
143
- const startSegment = options.first || this.internal.initialSegment;
144
- const lastSegment = options.last;
148
+ const startSegment = resolvedOptions.first || this.internal.initialSegment;
149
+ const lastSegment = resolvedOptions.last;
145
150
 
146
151
  let item = null;
147
152
  let index = 0;
@@ -206,7 +211,7 @@ class CodePath {
206
211
 
207
212
  // Call the callback when the first time.
208
213
  if (!skippedSegment) {
209
- callback.call(this, segment, controller);
214
+ resolvedCallback.call(this, segment, controller);
210
215
  if (segment === lastSegment) {
211
216
  controller.skip();
212
217
  }
@@ -46,19 +46,15 @@ function isReachable(segment) {
46
46
  function makeSegments(context, begin, end, create) {
47
47
  const list = context.segmentsList;
48
48
 
49
- if (begin < 0) {
50
- begin = list.length + begin;
51
- }
52
- if (end < 0) {
53
- end = list.length + end;
54
- }
49
+ const normalizedBegin = begin >= 0 ? begin : list.length + begin;
50
+ const normalizedEnd = end >= 0 ? end : list.length + end;
55
51
 
56
52
  const segments = [];
57
53
 
58
54
  for (let i = 0; i < context.count; ++i) {
59
55
  const allPrevSegments = [];
60
56
 
61
- for (let j = begin; j <= end; ++j) {
57
+ for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
62
58
  allPrevSegments.push(list[j][i]);
63
59
  }
64
60
 
@@ -79,18 +75,20 @@ function makeSegments(context, begin, end, create) {
79
75
  * @returns {CodePathSegment[]} The merged segments.
80
76
  */
81
77
  function mergeExtraSegments(context, segments) {
82
- while (segments.length > context.count) {
78
+ let currentSegments = segments;
79
+
80
+ while (currentSegments.length > context.count) {
83
81
  const merged = [];
84
82
 
85
- for (let i = 0, length = segments.length / 2 | 0; i < length; ++i) {
83
+ for (let i = 0, length = currentSegments.length / 2 | 0; i < length; ++i) {
86
84
  merged.push(CodePathSegment.newNext(
87
85
  context.idGenerator.next(),
88
- [segments[i], segments[i + length]]
86
+ [currentSegments[i], currentSegments[i + length]]
89
87
  ));
90
88
  }
91
- segments = merged;
89
+ currentSegments = merged;
92
90
  }
93
- return segments;
91
+ return currentSegments;
94
92
  }
95
93
 
96
94
  //------------------------------------------------------------------------------
@@ -400,25 +400,29 @@ function applyExtends(config, configContext, filePath, relativeTo) {
400
400
  }
401
401
 
402
402
  // Make the last element in an array take the highest precedence
403
- config = configExtends.reduceRight((previousValue, parentPath) => {
403
+ return configExtends.reduceRight((previousValue, parentPath) => {
404
404
  try {
405
+ let extensionPath;
406
+
405
407
  if (parentPath.startsWith("eslint:")) {
406
- parentPath = getEslintCoreConfigPath(parentPath);
408
+ extensionPath = getEslintCoreConfigPath(parentPath);
407
409
  } else if (isFilePath(parentPath)) {
408
410
 
409
411
  /*
410
412
  * If the `extends` path is relative, use the directory of the current configuration
411
413
  * file as the reference point. Otherwise, use as-is.
412
414
  */
413
- parentPath = (path.isAbsolute(parentPath)
415
+ extensionPath = (path.isAbsolute(parentPath)
414
416
  ? parentPath
415
417
  : path.join(relativeTo || path.dirname(filePath), parentPath)
416
418
  );
419
+ } else {
420
+ extensionPath = parentPath;
417
421
  }
418
- debug(`Loading ${parentPath}`);
422
+ debug(`Loading ${extensionPath}`);
419
423
 
420
424
  // eslint-disable-next-line no-use-before-define
421
- return ConfigOps.merge(load(parentPath, configContext, relativeTo), previousValue);
425
+ return ConfigOps.merge(load(extensionPath, configContext, relativeTo), previousValue);
422
426
  } catch (e) {
423
427
 
424
428
  /*
@@ -432,8 +436,6 @@ function applyExtends(config, configContext, filePath, relativeTo) {
432
436
  }
433
437
 
434
438
  }, config);
435
-
436
- return config;
437
439
  }
438
440
 
439
441
  /**
@@ -463,13 +465,20 @@ function resolve(filePath, relativeTo) {
463
465
 
464
466
  normalizedPackageName = naming.normalizePackageName(pluginName, "eslint-plugin");
465
467
  debug(`Attempting to resolve ${normalizedPackageName}`);
466
- filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
467
- return { filePath, configName, configFullName };
468
+
469
+ return {
470
+ filePath: resolver.resolve(normalizedPackageName, getLookupPath(relativeTo)),
471
+ configName,
472
+ configFullName
473
+ };
468
474
  }
469
475
  normalizedPackageName = naming.normalizePackageName(filePath, "eslint-config");
470
476
  debug(`Attempting to resolve ${normalizedPackageName}`);
471
- filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
472
- return { filePath, configFullName: filePath };
477
+
478
+ return {
479
+ filePath: resolver.resolve(normalizedPackageName, getLookupPath(relativeTo)),
480
+ configFullName: filePath
481
+ };
473
482
 
474
483
 
475
484
  }
@@ -136,29 +136,27 @@ module.exports = {
136
136
  const array = Array.isArray(src) || Array.isArray(target);
137
137
  let dst = array && [] || {};
138
138
 
139
- combine = !!combine;
140
- isRule = !!isRule;
141
139
  if (array) {
142
- target = target || [];
140
+ const resolvedTarget = target || [];
143
141
 
144
142
  // src could be a string, so check for array
145
143
  if (isRule && Array.isArray(src) && src.length > 1) {
146
144
  dst = dst.concat(src);
147
145
  } else {
148
- dst = dst.concat(target);
146
+ dst = dst.concat(resolvedTarget);
149
147
  }
150
- if (typeof src !== "object" && !Array.isArray(src)) {
151
- src = [src];
152
- }
153
- Object.keys(src).forEach((e, i) => {
154
- e = src[i];
148
+ const resolvedSrc = typeof src === "object" ? src : [src];
149
+
150
+ Object.keys(resolvedSrc).forEach((_, i) => {
151
+ const e = resolvedSrc[i];
152
+
155
153
  if (typeof dst[i] === "undefined") {
156
154
  dst[i] = e;
157
155
  } else if (typeof e === "object") {
158
156
  if (isRule) {
159
157
  dst[i] = e;
160
158
  } else {
161
- dst[i] = deepmerge(target[i], e, combine, isRule);
159
+ dst[i] = deepmerge(resolvedTarget[i], e, combine, isRule);
162
160
  }
163
161
  } else {
164
162
  if (!combine) {
@@ -197,11 +197,10 @@ class RuleConfigSet {
197
197
  * Add a severity level to the front of all configs in the instance.
198
198
  * This should only be called after all configs have been added to the instance.
199
199
  *
200
- * @param {number} [severity=2] The level of severity for the rule (0, 1, 2)
201
200
  * @returns {void}
202
201
  */
203
- addErrorSeverity(severity) {
204
- severity = severity || 2;
202
+ addErrorSeverity() {
203
+ const severity = 2;
205
204
 
206
205
  this.ruleConfigs = this.ruleConfigs.map(config => {
207
206
  config.unshift(severity);
@@ -120,6 +120,26 @@ class Plugins {
120
120
  throw pluginLoadErr;
121
121
  }
122
122
 
123
+ // This step is costly, so skip if debug is disabled
124
+ if (debug.enabled) {
125
+ const resolvedPath = require.resolve(longName);
126
+
127
+ let version = null;
128
+
129
+ try {
130
+ version = require(`${longName}/package.json`).version;
131
+ } catch (e) {
132
+
133
+ // Do nothing
134
+ }
135
+
136
+ const loadedPluginAndVersion = version
137
+ ? `${longName}@${version}`
138
+ : `${longName}, version unknown`;
139
+
140
+ debug(`Loaded plugin ${pluginName} (${loadedPluginAndVersion}) (from ${resolvedPath})`);
141
+ }
142
+
123
143
  this.define(pluginName, plugin);
124
144
  }
125
145
  }
package/lib/config.js CHANGED
@@ -51,11 +51,11 @@ function hasRules(options) {
51
51
  class Config {
52
52
 
53
53
  /**
54
- * @param {Object} options Options to be passed in
54
+ * @param {Object} providedOptions Options to be passed in
55
55
  * @param {Linter} linterContext Linter instance object
56
56
  */
57
- constructor(options, linterContext) {
58
- options = options || {};
57
+ constructor(providedOptions, linterContext) {
58
+ const options = providedOptions || {};
59
59
 
60
60
  this.linterContext = linterContext;
61
61
  this.plugins = new Plugins(linterContext.environments, linterContext.rules);
@@ -132,11 +132,10 @@ class Config {
132
132
  isResolvable(`eslint-config-${config}`) ||
133
133
  config.charAt(0) === "@";
134
134
 
135
- if (!isNamedConfig) {
136
- config = path.resolve(this.options.cwd, config);
137
- }
138
-
139
- this.specificConfig = ConfigFile.load(config, this);
135
+ this.specificConfig = ConfigFile.load(
136
+ isNamedConfig ? config : path.resolve(this.options.cwd, config),
137
+ this
138
+ );
140
139
  }
141
140
  }
142
141