eslint 7.0.0-alpha.1 → 7.0.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 (84) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +7 -7
  3. package/bin/eslint.js +115 -77
  4. package/conf/category-list.json +2 -3
  5. package/conf/environments.js +2 -1
  6. package/conf/eslint-recommended.js +3 -0
  7. package/lib/api.js +2 -0
  8. package/lib/cli-engine/cascading-config-array-factory.js +16 -2
  9. package/lib/cli-engine/cli-engine.js +53 -47
  10. package/lib/cli-engine/config-array/config-array.js +30 -1
  11. package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
  12. package/lib/cli-engine/config-array-factory.js +244 -235
  13. package/lib/cli.js +181 -95
  14. package/lib/eslint/eslint.js +656 -0
  15. package/lib/eslint/index.js +7 -0
  16. package/lib/init/autoconfig.js +4 -4
  17. package/lib/init/config-file.js +2 -2
  18. package/lib/init/config-initializer.js +2 -4
  19. package/lib/init/source-code-utils.js +2 -2
  20. package/lib/linter/node-event-generator.js +2 -2
  21. package/lib/options.js +0 -1
  22. package/lib/rule-tester/rule-tester.js +178 -23
  23. package/lib/rules/accessor-pairs.js +2 -2
  24. package/lib/rules/array-callback-return.js +3 -18
  25. package/lib/rules/arrow-body-style.js +26 -15
  26. package/lib/rules/callback-return.js +4 -0
  27. package/lib/rules/camelcase.js +38 -1
  28. package/lib/rules/comma-style.js +3 -8
  29. package/lib/rules/computed-property-spacing.js +2 -2
  30. package/lib/rules/curly.js +124 -40
  31. package/lib/rules/func-call-spacing.js +4 -3
  32. package/lib/rules/func-names.js +31 -24
  33. package/lib/rules/getter-return.js +2 -12
  34. package/lib/rules/global-require.js +4 -0
  35. package/lib/rules/handle-callback-err.js +4 -0
  36. package/lib/rules/id-blacklist.js +140 -64
  37. package/lib/rules/id-length.js +14 -4
  38. package/lib/rules/indent-legacy.js +0 -16
  39. package/lib/rules/key-spacing.js +1 -1
  40. package/lib/rules/new-cap.js +1 -1
  41. package/lib/rules/newline-per-chained-call.js +6 -3
  42. package/lib/rules/no-alert.js +5 -3
  43. package/lib/rules/no-buffer-constructor.js +4 -0
  44. package/lib/rules/no-dupe-else-if.js +1 -1
  45. package/lib/rules/no-empty-function.js +4 -2
  46. package/lib/rules/no-eval.js +3 -2
  47. package/lib/rules/no-extra-bind.js +1 -1
  48. package/lib/rules/no-extra-boolean-cast.js +168 -38
  49. package/lib/rules/no-extra-parens.js +9 -5
  50. package/lib/rules/no-implied-eval.js +83 -101
  51. package/lib/rules/no-import-assign.js +1 -1
  52. package/lib/rules/no-inner-declarations.js +31 -39
  53. package/lib/rules/no-lone-blocks.js +1 -1
  54. package/lib/rules/no-magic-numbers.js +72 -37
  55. package/lib/rules/no-mixed-requires.js +4 -0
  56. package/lib/rules/no-new-object.js +15 -3
  57. package/lib/rules/no-new-require.js +4 -0
  58. package/lib/rules/no-new-wrappers.js +1 -1
  59. package/lib/rules/no-obj-calls.js +24 -5
  60. package/lib/rules/no-path-concat.js +4 -0
  61. package/lib/rules/no-plusplus.js +39 -3
  62. package/lib/rules/no-process-env.js +4 -0
  63. package/lib/rules/no-process-exit.js +4 -0
  64. package/lib/rules/no-prototype-builtins.js +1 -1
  65. package/lib/rules/no-restricted-modules.js +52 -18
  66. package/lib/rules/no-setter-return.js +1 -1
  67. package/lib/rules/no-sync.js +4 -0
  68. package/lib/rules/no-underscore-dangle.js +1 -1
  69. package/lib/rules/no-unexpected-multiline.js +22 -12
  70. package/lib/rules/no-useless-concat.js +1 -1
  71. package/lib/rules/operator-assignment.js +3 -3
  72. package/lib/rules/operator-linebreak.js +4 -16
  73. package/lib/rules/prefer-numeric-literals.js +3 -3
  74. package/lib/rules/prefer-object-spread.js +2 -2
  75. package/lib/rules/require-await.js +1 -1
  76. package/lib/rules/space-before-function-paren.js +5 -2
  77. package/lib/rules/template-curly-spacing.js +59 -42
  78. package/lib/rules/utils/ast-utils.js +65 -4
  79. package/lib/rules/wrap-iife.js +54 -17
  80. package/lib/rules/yoda.js +101 -51
  81. package/lib/shared/relative-module-resolver.js +1 -0
  82. package/lib/shared/types.js +9 -2
  83. package/messages/plugin-conflict.txt +7 -0
  84. package/package.json +27 -26
@@ -45,7 +45,7 @@ function sortByKey(a, b) {
45
45
  function writeJSONConfigFile(config, filePath) {
46
46
  debug(`Writing JSON config file: ${filePath}`);
47
47
 
48
- const content = stringify(config, { cmp: sortByKey, space: 4 });
48
+ const content = `${stringify(config, { cmp: sortByKey, space: 4 })}\n`;
49
49
 
50
50
  fs.writeFileSync(filePath, content, "utf8");
51
51
  }
@@ -80,7 +80,7 @@ function writeJSConfigFile(config, filePath) {
80
80
  debug(`Writing JS config file: ${filePath}`);
81
81
 
82
82
  let contentToWrite;
83
- const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })};`;
83
+ const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })};\n`;
84
84
 
85
85
  try {
86
86
  const { CLIEngine } = require("../cli-engine");
@@ -15,6 +15,7 @@ const util = require("util"),
15
15
  inquirer = require("inquirer"),
16
16
  ProgressBar = require("progress"),
17
17
  semver = require("semver"),
18
+ espree = require("espree"),
18
19
  recConfig = require("../../conf/eslint-recommended"),
19
20
  ConfigOps = require("../shared/config-ops"),
20
21
  log = require("../shared/logging"),
@@ -31,8 +32,6 @@ const debug = require("debug")("eslint:config-initializer");
31
32
  // Private
32
33
  //------------------------------------------------------------------------------
33
34
 
34
- const DEFAULT_ECMA_VERSION = 2018;
35
-
36
35
  /* istanbul ignore next: hard to test fs function */
37
36
  /**
38
37
  * Create .eslintrc file in the current working directory
@@ -265,8 +264,7 @@ function processAnswers(answers) {
265
264
  extends: []
266
265
  };
267
266
 
268
- // set the latest ECMAScript version
269
- config.parserOptions.ecmaVersion = DEFAULT_ECMA_VERSION;
267
+ config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
270
268
  config.env.es6 = true;
271
269
  config.globals = {
272
270
  Atomics: "readonly",
@@ -23,7 +23,7 @@ const { CLIEngine } = require("../cli-engine");
23
23
  * TODO1: Expose the API that enumerates target files.
24
24
  * TODO2: Extract the creation logic of `SourceCode` from `Linter` class.
25
25
  */
26
- const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line no-restricted-modules
26
+ const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require
27
27
 
28
28
  const debug = require("debug")("eslint:source-code-utils");
29
29
 
@@ -97,7 +97,7 @@ function getSourceCodeOfFiles(patterns, options, callback) {
97
97
  sourceCodes[filename] = sourceCode;
98
98
  }
99
99
  if (callback) {
100
- callback(filenames.length); // eslint-disable-line callback-return
100
+ callback(filenames.length); // eslint-disable-line node/callback-return
101
101
  }
102
102
  });
103
103
 
@@ -159,8 +159,8 @@ function tryParseSelector(rawSelector) {
159
159
  try {
160
160
  return esquery.parse(rawSelector.replace(/:exit$/u, ""));
161
161
  } catch (err) {
162
- if (typeof err.offset === "number") {
163
- throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
162
+ if (err.location && err.location.start && typeof err.location.start.offset === "number") {
163
+ throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`);
164
164
  }
165
165
  throw err;
166
166
  }
package/lib/options.js CHANGED
@@ -46,7 +46,6 @@ module.exports = optionator({
46
46
  {
47
47
  option: "ext",
48
48
  type: "[String]",
49
- default: ".js",
50
49
  description: "Specify JavaScript file extensions"
51
50
  },
52
51
  {
@@ -45,16 +45,20 @@ const
45
45
  path = require("path"),
46
46
  util = require("util"),
47
47
  lodash = require("lodash"),
48
+ Traverser = require("../../lib/shared/traverser"),
48
49
  { getRuleOptionsSchema, validate } = require("../shared/config-validator"),
49
50
  { Linter, SourceCodeFixer, interpolate } = require("../linter");
50
51
 
51
52
  const ajv = require("../shared/ajv")({ strictDefaults: true });
52
53
 
54
+ const espreePath = require.resolve("espree");
53
55
 
54
56
  //------------------------------------------------------------------------------
55
57
  // Typedefs
56
58
  //------------------------------------------------------------------------------
57
59
 
60
+ /** @typedef {import("../shared/types").Parser} Parser */
61
+
58
62
  /**
59
63
  * A test case that is expected to pass lint.
60
64
  * @typedef {Object} ValidTestCase
@@ -119,6 +123,33 @@ const RuleTesterParameters = [
119
123
  "output"
120
124
  ];
121
125
 
126
+ /*
127
+ * All allowed property names in error objects.
128
+ */
129
+ const errorObjectParameters = new Set([
130
+ "message",
131
+ "messageId",
132
+ "data",
133
+ "type",
134
+ "line",
135
+ "column",
136
+ "endLine",
137
+ "endColumn",
138
+ "suggestions"
139
+ ]);
140
+ const friendlyErrorObjectParameterList = `[${[...errorObjectParameters].map(key => `'${key}'`).join(", ")}]`;
141
+
142
+ /*
143
+ * All allowed property names in suggestion objects.
144
+ */
145
+ const suggestionObjectParameters = new Set([
146
+ "desc",
147
+ "messageId",
148
+ "data",
149
+ "output"
150
+ ]);
151
+ const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
152
+
122
153
  const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
123
154
 
124
155
  /**
@@ -179,6 +210,70 @@ function sanitize(text) {
179
210
  );
180
211
  }
181
212
 
213
+ /**
214
+ * Define `start`/`end` properties as throwing error.
215
+ * @param {string} objName Object name used for error messages.
216
+ * @param {ASTNode} node The node to define.
217
+ * @returns {void}
218
+ */
219
+ function defineStartEndAsError(objName, node) {
220
+ Object.defineProperties(node, {
221
+ start: {
222
+ get() {
223
+ throw new Error(`Use ${objName}.range[0] instead of ${objName}.start`);
224
+ },
225
+ configurable: true,
226
+ enumerable: false
227
+ },
228
+ end: {
229
+ get() {
230
+ throw new Error(`Use ${objName}.range[1] instead of ${objName}.end`);
231
+ },
232
+ configurable: true,
233
+ enumerable: false
234
+ }
235
+ });
236
+ }
237
+
238
+ /**
239
+ * Define `start`/`end` properties of all nodes of the given AST as throwing error.
240
+ * @param {ASTNode} ast The root node to errorize `start`/`end` properties.
241
+ * @param {Object} [visitorKeys] Visitor keys to be used for traversing the given ast.
242
+ * @returns {void}
243
+ */
244
+ function defineStartEndAsErrorInTree(ast, visitorKeys) {
245
+ Traverser.traverse(ast, { visitorKeys, enter: defineStartEndAsError.bind(null, "node") });
246
+ ast.tokens.forEach(defineStartEndAsError.bind(null, "token"));
247
+ ast.comments.forEach(defineStartEndAsError.bind(null, "token"));
248
+ }
249
+
250
+ /**
251
+ * Wraps the given parser in order to intercept and modify return values from the `parse` and `parseForESLint` methods, for test purposes.
252
+ * In particular, to modify ast nodes, tokens and comments to throw on access to their `start` and `end` properties.
253
+ * @param {Parser} parser Parser object.
254
+ * @returns {Parser} Wrapped parser object.
255
+ */
256
+ function wrapParser(parser) {
257
+ if (typeof parser.parseForESLint === "function") {
258
+ return {
259
+ parseForESLint(...args) {
260
+ const ret = parser.parseForESLint(...args);
261
+
262
+ defineStartEndAsErrorInTree(ret.ast, ret.visitorKeys);
263
+ return ret;
264
+ }
265
+ };
266
+ }
267
+ return {
268
+ parse(...args) {
269
+ const ast = parser.parse(...args);
270
+
271
+ defineStartEndAsErrorInTree(ast);
272
+ return ast;
273
+ }
274
+ };
275
+ }
276
+
182
277
  //------------------------------------------------------------------------------
183
278
  // Public Interface
184
279
  //------------------------------------------------------------------------------
@@ -423,9 +518,12 @@ class RuleTester {
423
518
 
424
519
  if (typeof config.parser === "string") {
425
520
  assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
426
- linter.defineParser(config.parser, require(config.parser));
521
+ } else {
522
+ config.parser = espreePath;
427
523
  }
428
524
 
525
+ linter.defineParser(config.parser, wrapParser(require(config.parser)));
526
+
429
527
  if (schema) {
430
528
  ajv.validateSchema(schema);
431
529
 
@@ -456,20 +554,21 @@ class RuleTester {
456
554
 
457
555
  // Verify the code.
458
556
  const messages = linter.verify(code, config, filename);
557
+ const fatalErrorMessage = messages.find(m => m.fatal);
459
558
 
460
- // Ignore syntax errors for backward compatibility if `errors` is a number.
461
- if (typeof item.errors !== "number") {
462
- const errorMessage = messages.find(m => m.fatal);
463
-
464
- assert(!errorMessage, `A fatal parsing error occurred: ${errorMessage && errorMessage.message}`);
465
- }
559
+ assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
466
560
 
467
561
  // Verify if autofix makes a syntax error or not.
468
562
  if (messages.some(m => m.fix)) {
469
563
  output = SourceCodeFixer.applyFixes(code, messages).output;
470
564
  const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal);
471
565
 
472
- assert(!errorMessageInFix, `A fatal parsing error occurred in autofix: ${errorMessageInFix && errorMessageInFix.message}`);
566
+ assert(!errorMessageInFix, [
567
+ "A fatal parsing error occurred in autofix.",
568
+ `Error: ${errorMessageInFix && errorMessageInFix.message}`,
569
+ "Autofix output:",
570
+ output
571
+ ].join("\n"));
473
572
  } else {
474
573
  output = code;
475
574
  }
@@ -545,10 +644,12 @@ class RuleTester {
545
644
  assert.ok(item.errors || item.errors === 0,
546
645
  `Did not specify errors for an invalid test of ${ruleName}`);
547
646
 
647
+ const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
648
+ const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
649
+
548
650
  const result = runRuleForItem(item);
549
651
  const messages = result.messages;
550
652
 
551
-
552
653
  if (typeof item.errors === "number") {
553
654
  assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
554
655
  item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
@@ -573,25 +674,31 @@ class RuleTester {
573
674
 
574
675
  // Just an error message.
575
676
  assertMessageMatches(message.message, error);
576
- } else if (typeof error === "object") {
677
+ } else if (typeof error === "object" && error !== null) {
577
678
 
578
679
  /*
579
680
  * Error object.
580
681
  * This may have a message, messageId, data, node type, line, and/or
581
682
  * column.
582
683
  */
684
+
685
+ Object.keys(error).forEach(propertyName => {
686
+ assert.ok(
687
+ errorObjectParameters.has(propertyName),
688
+ `Invalid error property name '${propertyName}'. Expected one of ${friendlyErrorObjectParameterList}.`
689
+ );
690
+ });
691
+
583
692
  if (hasOwnProperty(error, "message")) {
584
693
  assert.ok(!hasOwnProperty(error, "messageId"), "Error should not specify both 'message' and a 'messageId'.");
585
694
  assert.ok(!hasOwnProperty(error, "data"), "Error should not specify both 'data' and 'message'.");
586
695
  assertMessageMatches(message.message, error.message);
587
696
  } else if (hasOwnProperty(error, "messageId")) {
588
697
  assert.ok(
589
- hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages"),
698
+ ruleHasMetaMessages,
590
699
  "Error can not use 'messageId' if rule under test doesn't define 'meta.messages'."
591
700
  );
592
701
  if (!hasOwnProperty(rule.meta.messages, error.messageId)) {
593
- const friendlyIDList = `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]`;
594
-
595
702
  assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
596
703
  }
597
704
  assert.strictEqual(
@@ -654,20 +761,62 @@ class RuleTester {
654
761
  assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
655
762
 
656
763
  error.suggestions.forEach((expectedSuggestion, index) => {
764
+ assert.ok(
765
+ typeof expectedSuggestion === "object" && expectedSuggestion !== null,
766
+ "Test suggestion in 'suggestions' array must be an object."
767
+ );
768
+ Object.keys(expectedSuggestion).forEach(propertyName => {
769
+ assert.ok(
770
+ suggestionObjectParameters.has(propertyName),
771
+ `Invalid suggestion property name '${propertyName}'. Expected one of ${friendlySuggestionObjectParameterList}.`
772
+ );
773
+ });
774
+
657
775
  const actualSuggestion = message.suggestions[index];
776
+ const suggestionPrefix = `Error Suggestion at index ${index} :`;
777
+
778
+ if (hasOwnProperty(expectedSuggestion, "desc")) {
779
+ assert.ok(
780
+ !hasOwnProperty(expectedSuggestion, "data"),
781
+ `${suggestionPrefix} Test should not specify both 'desc' and 'data'.`
782
+ );
783
+ assert.strictEqual(
784
+ actualSuggestion.desc,
785
+ expectedSuggestion.desc,
786
+ `${suggestionPrefix} desc should be "${expectedSuggestion.desc}" but got "${actualSuggestion.desc}" instead.`
787
+ );
788
+ }
658
789
 
659
- /**
660
- * Tests equality of a suggestion key if that key is defined in the expected output.
661
- * @param {string} key Key to validate from the suggestion object
662
- * @returns {void}
663
- */
664
- function assertSuggestionKeyEquals(key) {
665
- if (hasOwnProperty(expectedSuggestion, key)) {
666
- assert.deepStrictEqual(actualSuggestion[key], expectedSuggestion[key], `Error suggestion at index: ${index} should have desc of: "${actualSuggestion[key]}"`);
790
+ if (hasOwnProperty(expectedSuggestion, "messageId")) {
791
+ assert.ok(
792
+ ruleHasMetaMessages,
793
+ `${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`
794
+ );
795
+ assert.ok(
796
+ hasOwnProperty(rule.meta.messages, expectedSuggestion.messageId),
797
+ `${suggestionPrefix} Test has invalid messageId '${expectedSuggestion.messageId}', the rule under test allows only one of ${friendlyIDList}.`
798
+ );
799
+ assert.strictEqual(
800
+ actualSuggestion.messageId,
801
+ expectedSuggestion.messageId,
802
+ `${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`
803
+ );
804
+ if (hasOwnProperty(expectedSuggestion, "data")) {
805
+ const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
806
+ const rehydratedDesc = interpolate(unformattedMetaMessage, expectedSuggestion.data);
807
+
808
+ assert.strictEqual(
809
+ actualSuggestion.desc,
810
+ rehydratedDesc,
811
+ `${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`
812
+ );
667
813
  }
814
+ } else {
815
+ assert.ok(
816
+ !hasOwnProperty(expectedSuggestion, "data"),
817
+ `${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`
818
+ );
668
819
  }
669
- assertSuggestionKeyEquals("desc");
670
- assertSuggestionKeyEquals("messageId");
671
820
 
672
821
  if (hasOwnProperty(expectedSuggestion, "output")) {
673
822
  const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output;
@@ -695,6 +844,12 @@ class RuleTester {
695
844
  } else {
696
845
  assert.strictEqual(result.output, item.output, "Output is incorrect.");
697
846
  }
847
+ } else {
848
+ assert.strictEqual(
849
+ result.output,
850
+ item.code,
851
+ "The rule fixed the code. Please add 'output' property."
852
+ );
698
853
  }
699
854
 
700
855
  assertASTDidntChange(result.beforeAST, result.afterAST);
@@ -171,7 +171,7 @@ module.exports = {
171
171
  },
172
172
  enforceForClassMembers: {
173
173
  type: "boolean",
174
- default: false
174
+ default: true
175
175
  }
176
176
  },
177
177
  additionalProperties: false
@@ -190,7 +190,7 @@ module.exports = {
190
190
  const config = context.options[0] || {};
191
191
  const checkGetWithoutSet = config.getWithoutSet === true;
192
192
  const checkSetWithoutGet = config.setWithoutGet !== false;
193
- const enforceForClassMembers = config.enforceForClassMembers === true;
193
+ const enforceForClassMembers = config.enforceForClassMembers !== false;
194
194
  const sourceCode = context.getSourceCode();
195
195
 
196
196
  /**
@@ -29,22 +29,6 @@ function isReachable(segment) {
29
29
  return segment.reachable;
30
30
  }
31
31
 
32
- /**
33
- * Gets a readable location.
34
- *
35
- * - FunctionExpression -> the function name or `function` keyword.
36
- * - ArrowFunctionExpression -> `=>` token.
37
- * @param {ASTNode} node A function node to get.
38
- * @param {SourceCode} sourceCode A source code to get tokens.
39
- * @returns {ASTNode|Token} The node or the token of a location.
40
- */
41
- function getLocation(node, sourceCode) {
42
- if (node.type === "ArrowFunctionExpression") {
43
- return sourceCode.getTokenBefore(node.body);
44
- }
45
- return node.id || node;
46
- }
47
-
48
32
  /**
49
33
  * Checks a given node is a MemberExpression node which has the specified name's
50
34
  * property.
@@ -179,6 +163,7 @@ module.exports = {
179
163
  create(context) {
180
164
 
181
165
  const options = context.options[0] || { allowImplicit: false, checkForEach: false };
166
+ const sourceCode = context.getSourceCode();
182
167
 
183
168
  let funcInfo = {
184
169
  arrayMethodName: null,
@@ -217,12 +202,12 @@ module.exports = {
217
202
  }
218
203
 
219
204
  if (messageId) {
220
- let name = astUtils.getFunctionNameWithKind(funcInfo.node);
205
+ let name = astUtils.getFunctionNameWithKind(node);
221
206
 
222
207
  name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
223
208
  context.report({
224
209
  node,
225
- loc: getLocation(node, context.getSourceCode()).loc.start,
210
+ loc: astUtils.getFunctionHeadLoc(node, sourceCode),
226
211
  messageId,
227
212
  data: { name }
228
213
  });
@@ -91,7 +91,7 @@ module.exports = {
91
91
  * @returns {Token} The found closing parenthesis token.
92
92
  */
93
93
  function findClosingParen(token) {
94
- let node = sourceCode.getNodeByRangeIndex(token.range[1]);
94
+ let node = sourceCode.getNodeByRangeIndex(token.range[0]);
95
95
 
96
96
  while (!astUtils.isParenthesised(sourceCode, node)) {
97
97
  node = node.parent;
@@ -206,24 +206,35 @@ module.exports = {
206
206
  fix(fixer) {
207
207
  const fixes = [];
208
208
  const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
209
- const firstBodyToken = sourceCode.getTokenAfter(arrowToken);
210
- const lastBodyToken = sourceCode.getLastToken(node);
209
+ const [firstTokenAfterArrow, secondTokenAfterArrow] = sourceCode.getTokensAfter(arrowToken, { count: 2 });
210
+ const lastToken = sourceCode.getLastToken(node);
211
211
  const isParenthesisedObjectLiteral =
212
- astUtils.isOpeningParenToken(firstBodyToken) &&
213
- astUtils.isOpeningBraceToken(sourceCode.getTokenAfter(firstBodyToken));
214
-
215
- // Wrap the value by a block and a return statement.
216
- fixes.push(
217
- fixer.insertTextBefore(firstBodyToken, "{return "),
218
- fixer.insertTextAfter(lastBodyToken, "}")
219
- );
212
+ astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
213
+ astUtils.isOpeningBraceToken(secondTokenAfterArrow);
220
214
 
221
215
  // If the value is object literal, remove parentheses which were forced by syntax.
222
216
  if (isParenthesisedObjectLiteral) {
223
- fixes.push(
224
- fixer.remove(firstBodyToken),
225
- fixer.remove(findClosingParen(firstBodyToken))
226
- );
217
+ const openingParenToken = firstTokenAfterArrow;
218
+ const openingBraceToken = secondTokenAfterArrow;
219
+
220
+ if (astUtils.isTokenOnSameLine(openingParenToken, openingBraceToken)) {
221
+ fixes.push(fixer.replaceText(openingParenToken, "{return "));
222
+ } else {
223
+
224
+ // Avoid ASI
225
+ fixes.push(
226
+ fixer.replaceText(openingParenToken, "{"),
227
+ fixer.insertTextBefore(openingBraceToken, "return ")
228
+ );
229
+ }
230
+
231
+ // Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
232
+ fixes.push(fixer.remove(findClosingParen(openingBraceToken)));
233
+ fixes.push(fixer.insertTextAfter(lastToken, "}"));
234
+
235
+ } else {
236
+ fixes.push(fixer.insertTextBefore(firstTokenAfterArrow, "{return "));
237
+ fixes.push(fixer.insertTextAfter(lastToken, "}"));
227
238
  }
228
239
 
229
240
  return fixes;
@@ -10,6 +10,10 @@
10
10
 
11
11
  module.exports = {
12
12
  meta: {
13
+ deprecated: true,
14
+
15
+ replacedBy: ["node/callback-return"],
16
+
13
17
  type: "suggestion",
14
18
 
15
19
  docs: {
@@ -125,6 +125,40 @@ module.exports = {
125
125
  return false;
126
126
  }
127
127
 
128
+ /**
129
+ * Checks whether the given node represents assignment target property in destructuring.
130
+ *
131
+ * For examples:
132
+ * ({a: b.foo} = c); // => true for `foo`
133
+ * ([a.foo] = b); // => true for `foo`
134
+ * ([a.foo = 1] = b); // => true for `foo`
135
+ * ({...a.foo} = b); // => true for `foo`
136
+ * @param {ASTNode} node An Identifier node to check
137
+ * @returns {boolean} True if the node is an assignment target property in destructuring.
138
+ */
139
+ function isAssignmentTargetPropertyInDestructuring(node) {
140
+ if (
141
+ node.parent.type === "MemberExpression" &&
142
+ node.parent.property === node &&
143
+ !node.parent.computed
144
+ ) {
145
+ const effectiveParent = node.parent.parent;
146
+
147
+ return (
148
+ effectiveParent.type === "Property" &&
149
+ effectiveParent.value === node.parent &&
150
+ effectiveParent.parent.type === "ObjectPattern" ||
151
+ effectiveParent.type === "ArrayPattern" ||
152
+ effectiveParent.type === "RestElement" ||
153
+ (
154
+ effectiveParent.type === "AssignmentPattern" &&
155
+ effectiveParent.left === node.parent
156
+ )
157
+ );
158
+ }
159
+ return false;
160
+ }
161
+
128
162
  /**
129
163
  * Reports an AST node as a rule violation.
130
164
  * @param {ASTNode} node The node to report.
@@ -170,6 +204,9 @@ module.exports = {
170
204
  // Report AssignmentExpressions only if they are the left side of the assignment
171
205
  } else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
172
206
  report(node);
207
+
208
+ } else if (isAssignmentTargetPropertyInDestructuring(node) && nameIsUnderscored) {
209
+ report(node);
173
210
  }
174
211
 
175
212
  /*
@@ -186,7 +223,7 @@ module.exports = {
186
223
 
187
224
  const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
188
225
 
189
- if (isUnderscored(name) && node.parent.computed) {
226
+ if (nameIsUnderscored && node.parent.computed) {
190
227
  report(node);
191
228
  }
192
229
 
@@ -146,10 +146,7 @@ module.exports = {
146
146
  // lone comma
147
147
  context.report({
148
148
  node: reportItem,
149
- loc: {
150
- line: commaToken.loc.end.line,
151
- column: commaToken.loc.start.column
152
- },
149
+ loc: commaToken.loc,
153
150
  messageId: "unexpectedLineBeforeAndAfterComma",
154
151
  fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken)
155
152
  });
@@ -158,6 +155,7 @@ module.exports = {
158
155
 
159
156
  context.report({
160
157
  node: reportItem,
158
+ loc: commaToken.loc,
161
159
  messageId: "expectedCommaFirst",
162
160
  fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
163
161
  });
@@ -166,10 +164,7 @@ module.exports = {
166
164
 
167
165
  context.report({
168
166
  node: reportItem,
169
- loc: {
170
- line: commaToken.loc.end.line,
171
- column: commaToken.loc.end.column
172
- },
167
+ loc: commaToken.loc,
173
168
  messageId: "expectedCommaLast",
174
169
  fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
175
170
  });
@@ -32,7 +32,7 @@ module.exports = {
32
32
  properties: {
33
33
  enforceForClassMembers: {
34
34
  type: "boolean",
35
- default: false
35
+ default: true
36
36
  }
37
37
  },
38
38
  additionalProperties: false
@@ -51,7 +51,7 @@ module.exports = {
51
51
  create(context) {
52
52
  const sourceCode = context.getSourceCode();
53
53
  const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
54
- const enforceForClassMembers = context.options[1] && context.options[1].enforceForClassMembers;
54
+ const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;
55
55
 
56
56
  //--------------------------------------------------------------------------
57
57
  // Helpers