eslint 4.7.1 → 4.10.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 (65) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +34 -19
  3. package/conf/default-cli-options.js +2 -1
  4. package/conf/eslint-recommended.js +2 -0
  5. package/lib/ast-utils.js +2 -1
  6. package/lib/cli-engine.js +26 -5
  7. package/lib/cli.js +17 -9
  8. package/lib/code-path-analysis/code-path-segment.js +39 -39
  9. package/lib/code-path-analysis/code-path-state.js +3 -0
  10. package/lib/formatters/html-template-message.html +1 -1
  11. package/lib/formatters/html-template-page.html +3 -1
  12. package/lib/formatters/html.js +2 -1
  13. package/lib/ignored-paths.js +1 -1
  14. package/lib/linter.js +43 -71
  15. package/lib/logging.js +2 -2
  16. package/lib/options.js +12 -0
  17. package/lib/rules/array-bracket-newline.js +19 -5
  18. package/lib/rules/block-spacing.js +1 -1
  19. package/lib/rules/callback-return.js +2 -1
  20. package/lib/rules/capitalized-comments.js +2 -1
  21. package/lib/rules/comma-style.js +3 -1
  22. package/lib/rules/dot-notation.js +56 -35
  23. package/lib/rules/generator-star-spacing.js +3 -3
  24. package/lib/rules/indent-legacy.js +5 -2
  25. package/lib/rules/indent.js +25 -19
  26. package/lib/rules/lines-around-comment.js +33 -4
  27. package/lib/rules/lines-between-class-members.js +91 -0
  28. package/lib/rules/max-len.js +2 -3
  29. package/lib/rules/multiline-comment-style.js +294 -0
  30. package/lib/rules/new-cap.js +2 -1
  31. package/lib/rules/newline-before-return.js +4 -2
  32. package/lib/rules/no-alert.js +7 -15
  33. package/lib/rules/no-catch-shadow.js +1 -1
  34. package/lib/rules/no-constant-condition.js +2 -2
  35. package/lib/rules/no-control-regex.js +2 -1
  36. package/lib/rules/no-else-return.js +43 -8
  37. package/lib/rules/no-extra-parens.js +6 -3
  38. package/lib/rules/no-lonely-if.js +2 -1
  39. package/lib/rules/no-loop-func.js +2 -3
  40. package/lib/rules/no-mixed-requires.js +8 -4
  41. package/lib/rules/no-restricted-imports.js +86 -17
  42. package/lib/rules/no-restricted-modules.js +84 -15
  43. package/lib/rules/no-trailing-spaces.js +1 -1
  44. package/lib/rules/no-unneeded-ternary.js +3 -1
  45. package/lib/rules/no-unused-labels.js +2 -1
  46. package/lib/rules/no-useless-computed-key.js +2 -1
  47. package/lib/rules/no-useless-escape.js +8 -1
  48. package/lib/rules/no-var.js +11 -0
  49. package/lib/rules/object-shorthand.js +6 -2
  50. package/lib/rules/operator-linebreak.js +3 -1
  51. package/lib/rules/padding-line-between-statements.js +2 -2
  52. package/lib/rules/require-jsdoc.js +11 -18
  53. package/lib/rules/sort-imports.js +6 -3
  54. package/lib/rules/space-unary-ops.js +6 -8
  55. package/lib/rules/valid-jsdoc.js +39 -33
  56. package/lib/testers/rule-tester.js +20 -6
  57. package/lib/util/apply-disable-directives.js +56 -27
  58. package/lib/util/node-event-generator.js +6 -20
  59. package/lib/util/safe-emitter.js +54 -0
  60. package/lib/util/source-code.js +73 -67
  61. package/messages/no-config-found.txt +1 -1
  62. package/package.json +3 -4
  63. package/lib/internal-rules/.eslintrc.yml +0 -3
  64. package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
  65. package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
package/lib/linter.js CHANGED
@@ -9,8 +9,7 @@
9
9
  // Requirements
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- const EventEmitter = require("events").EventEmitter,
13
- eslintScope = require("eslint-scope"),
12
+ const eslintScope = require("eslint-scope"),
14
13
  levn = require("levn"),
15
14
  lodash = require("lodash"),
16
15
  blankScriptAST = require("../conf/blank-script.json"),
@@ -20,6 +19,7 @@ const EventEmitter = require("events").EventEmitter,
20
19
  validator = require("./config/config-validator"),
21
20
  Environments = require("./config/environments"),
22
21
  applyDisableDirectives = require("./util/apply-disable-directives"),
22
+ createEmitter = require("./util/safe-emitter"),
23
23
  NodeEventGenerator = require("./util/node-event-generator"),
24
24
  SourceCode = require("./util/source-code"),
25
25
  Traverser = require("./util/traverser"),
@@ -164,13 +164,12 @@ function parseListConfig(string) {
164
164
  * Ensures that variables representing built-in properties of the Global Object,
165
165
  * and any globals declared by special block comments, are present in the global
166
166
  * scope.
167
- * @param {ASTNode} program The top node of the AST.
168
167
  * @param {Scope} globalScope The global scope.
169
168
  * @param {Object} config The existing configuration data.
170
169
  * @param {Environments} envContext Env context
171
170
  * @returns {void}
172
171
  */
173
- function addDeclaredGlobals(program, globalScope, config, envContext) {
172
+ function addDeclaredGlobals(globalScope, config, envContext) {
174
173
  const declaredGlobals = {},
175
174
  exportedGlobals = {},
176
175
  explicitGlobals = {},
@@ -292,7 +291,7 @@ function createDisableDirectives(type, loc, value) {
292
291
  */
293
292
  function modifyConfigsFromComments(filename, ast, config, linterContext) {
294
293
 
295
- let commentConfig = {
294
+ const commentConfig = {
296
295
  exported: {},
297
296
  astGlobals: {},
298
297
  rules: {},
@@ -321,10 +320,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
321
320
  Object.assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
322
321
  break;
323
322
 
324
- case "eslint-env":
325
- Object.assign(commentConfig.env, parseListConfig(value));
326
- break;
327
-
328
323
  case "eslint-disable":
329
324
  [].push.apply(disableDirectives, createDisableDirectives("disable", comment.loc.start, value));
330
325
  break;
@@ -362,14 +357,6 @@ function modifyConfigsFromComments(filename, ast, config, linterContext) {
362
357
  }
363
358
  });
364
359
 
365
- // apply environment configs
366
- Object.keys(commentConfig.env).forEach(name => {
367
- const env = linterContext.environments.get(name);
368
-
369
- if (env) {
370
- commentConfig = ConfigOps.merge(commentConfig, env);
371
- }
372
- });
373
360
  Object.assign(commentConfig.rules, commentRules);
374
361
 
375
362
  return {
@@ -660,22 +647,6 @@ function markVariableAsUsed(scopeManager, currentNode, parserOptions, name) {
660
647
  return false;
661
648
  }
662
649
 
663
- /**
664
- * Gets all the ancestors of a given node
665
- * @param {ASTNode} node The node
666
- * @returns {ASTNode[]} All the ancestor nodes in the AST, not including the provided node, starting
667
- * from the root node and going inwards to the parent node.
668
- */
669
- function getAncestors(node) {
670
- if (node.parent) {
671
- const parentAncestors = getAncestors(node.parent);
672
-
673
- parentAncestors.push(node.parent);
674
- return parentAncestors;
675
- }
676
- return [];
677
- }
678
-
679
650
  // methods that exist on SourceCode object
680
651
  const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
681
652
  getSource: "getText",
@@ -714,6 +685,8 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
714
685
  )
715
686
  );
716
687
 
688
+ const lastSourceCodes = new WeakMap();
689
+
717
690
  //------------------------------------------------------------------------------
718
691
  // Public Interface
719
692
  //------------------------------------------------------------------------------
@@ -725,7 +698,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
725
698
  module.exports = class Linter {
726
699
 
727
700
  constructor() {
728
- this.sourceCode = null;
701
+ lastSourceCodes.set(this, null);
729
702
  this.version = pkg.version;
730
703
 
731
704
  this.rules = new Rules();
@@ -750,20 +723,24 @@ module.exports = class Linter {
750
723
  * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
751
724
  * If this is not set, the filename will default to '<input>' in the rule context. If
752
725
  * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
753
- * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
726
+ * @param {boolean} [filenameOrOptions.allowInlineConfig=true] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
754
727
  * Useful if you want to validate JS without comments overriding rules.
728
+ * @param {boolean} [filenameOrOptions.reportUnusedDisableDirectives=false] Adds reported errors for unused
729
+ * eslint-disable directives
755
730
  * @returns {Object[]} The results as an array of messages or null if no messages.
756
731
  */
757
732
  _verifyWithoutProcessors(textOrSourceCode, config, filenameOrOptions) {
758
733
  let text,
759
734
  parserServices,
760
735
  allowInlineConfig,
761
- providedFilename;
736
+ providedFilename,
737
+ reportUnusedDisableDirectives;
762
738
 
763
739
  // evaluate arguments
764
740
  if (typeof filenameOrOptions === "object") {
765
741
  providedFilename = filenameOrOptions.filename;
766
742
  allowInlineConfig = filenameOrOptions.allowInlineConfig;
743
+ reportUnusedDisableDirectives = filenameOrOptions.reportUnusedDisableDirectives;
767
744
  } else {
768
745
  providedFilename = filenameOrOptions;
769
746
  }
@@ -771,11 +748,11 @@ module.exports = class Linter {
771
748
  const filename = typeof providedFilename === "string" ? providedFilename : "<input>";
772
749
 
773
750
  if (typeof textOrSourceCode === "string") {
774
- this.sourceCode = null;
751
+ lastSourceCodes.set(this, null);
775
752
  text = textOrSourceCode;
776
753
  } else {
777
- this.sourceCode = textOrSourceCode;
778
- text = this.sourceCode.text;
754
+ lastSourceCodes.set(this, textOrSourceCode);
755
+ text = textOrSourceCode.text;
779
756
  }
780
757
 
781
758
  // search and apply "eslint-env *".
@@ -794,13 +771,13 @@ module.exports = class Linter {
794
771
  // process initial config to make it safe to extend
795
772
  config = prepareConfig(config, this.environments);
796
773
 
797
- if (this.sourceCode) {
774
+ if (lastSourceCodes.get(this)) {
798
775
  parserServices = {};
799
776
  } else {
800
777
 
801
778
  // there's no input, just exit here
802
779
  if (text.trim().length === 0) {
803
- this.sourceCode = new SourceCode(text, blankScriptAST);
780
+ lastSourceCodes.set(this, new SourceCode(text, blankScriptAST));
804
781
  return [];
805
782
  }
806
783
 
@@ -816,11 +793,11 @@ module.exports = class Linter {
816
793
  }
817
794
 
818
795
  parserServices = parseResult.services;
819
- this.sourceCode = new SourceCode(text, parseResult.ast);
796
+ lastSourceCodes.set(this, new SourceCode(text, parseResult.ast));
820
797
  }
821
798
 
822
799
  const problems = [];
823
- const sourceCode = this.sourceCode;
800
+ const sourceCode = lastSourceCodes.get(this);
824
801
  let disableDirectives;
825
802
 
826
803
  // parse global comments and modify config
@@ -834,7 +811,8 @@ module.exports = class Linter {
834
811
  disableDirectives = [];
835
812
  }
836
813
 
837
- const emitter = new EventEmitter().setMaxListeners(Infinity);
814
+ const emitter = createEmitter();
815
+ const traverser = new Traverser();
838
816
  const ecmaFeatures = config.parserOptions.ecmaFeatures || {};
839
817
  const ecmaVersion = config.parserOptions.ecmaVersion || 5;
840
818
  const scopeManager = eslintScope.analyze(sourceCode.ast, {
@@ -846,19 +824,6 @@ module.exports = class Linter {
846
824
  fallback: Traverser.getKeys
847
825
  });
848
826
 
849
- let currentNode = sourceCode.ast;
850
- const nodeQueue = [];
851
-
852
- new Traverser().traverse(sourceCode.ast, {
853
- enter(node, parent) {
854
- node.parent = parent;
855
- nodeQueue.push({ isEntering: true, node });
856
- },
857
- leave(node) {
858
- nodeQueue.push({ isEntering: false, node });
859
- }
860
- });
861
-
862
827
  /*
863
828
  * Create a frozen object with the ruleContext properties and methods that are shared by all rules.
864
829
  * All rule contexts will inherit from this object. This avoids the performance penalty of copying all the
@@ -868,12 +833,12 @@ module.exports = class Linter {
868
833
  Object.assign(
869
834
  Object.create(BASE_TRAVERSAL_CONTEXT),
870
835
  {
871
- getAncestors: () => getAncestors(currentNode),
836
+ getAncestors: () => traverser.parents(),
872
837
  getDeclaredVariables: scopeManager.getDeclaredVariables.bind(scopeManager),
873
838
  getFilename: () => filename,
874
- getScope: () => getScope(scopeManager, currentNode, config.parserOptions.ecmaVersion),
839
+ getScope: () => getScope(scopeManager, traverser.current(), config.parserOptions.ecmaVersion),
875
840
  getSourceCode: () => sourceCode,
876
- markVariableAsUsed: name => markVariableAsUsed(scopeManager, currentNode, config.parserOptions, name),
841
+ markVariableAsUsed: name => markVariableAsUsed(scopeManager, traverser.current(), config.parserOptions, name),
877
842
  parserOptions: config.parserOptions,
878
843
  parserPath: config.parser,
879
844
  parserServices,
@@ -889,7 +854,7 @@ module.exports = class Linter {
889
854
  */
890
855
  _linter: {
891
856
  report() {},
892
- on: emitter.on.bind(emitter)
857
+ on: emitter.on
893
858
  }
894
859
  }
895
860
  )
@@ -974,23 +939,30 @@ module.exports = class Linter {
974
939
  });
975
940
 
976
941
  // augment global scope with declared global variables
977
- addDeclaredGlobals(sourceCode.ast, scopeManager.scopes[0], config, this.environments);
942
+ addDeclaredGlobals(scopeManager.scopes[0], config, this.environments);
978
943
 
979
944
  const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter));
980
945
 
981
- nodeQueue.forEach(traversalInfo => {
982
- currentNode = traversalInfo.node;
983
-
984
- if (traversalInfo.isEntering) {
985
- eventGenerator.enterNode(currentNode);
986
- } else {
987
- eventGenerator.leaveNode(currentNode);
946
+ /*
947
+ * Each node has a type property. Whenever a particular type of
948
+ * node is found, an event is fired. This allows any listeners to
949
+ * automatically be informed that this type of node has been found
950
+ * and react accordingly.
951
+ */
952
+ traverser.traverse(sourceCode.ast, {
953
+ enter(node, parent) {
954
+ node.parent = parent;
955
+ eventGenerator.enterNode(node);
956
+ },
957
+ leave(node) {
958
+ eventGenerator.leaveNode(node);
988
959
  }
989
960
  });
990
961
 
991
962
  return applyDisableDirectives({
992
963
  directives: disableDirectives,
993
- problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column)
964
+ problems: problems.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
965
+ reportUnusedDisableDirectives
994
966
  });
995
967
  }
996
968
 
@@ -1028,7 +1000,7 @@ module.exports = class Linter {
1028
1000
  * @returns {SourceCode} The SourceCode object.
1029
1001
  */
1030
1002
  getSourceCode() {
1031
- return this.sourceCode;
1003
+ return lastSourceCodes.get(this);
1032
1004
  }
1033
1005
 
1034
1006
  /**
package/lib/logging.js CHANGED
@@ -15,7 +15,7 @@ module.exports = {
15
15
  * @returns {void}
16
16
  */
17
17
  info() {
18
- console.log.apply(console, Array.prototype.slice.call(arguments));
18
+ console.log.apply(console, arguments);
19
19
  },
20
20
 
21
21
  /**
@@ -23,6 +23,6 @@ module.exports = {
23
23
  * @returns {void}
24
24
  */
25
25
  error() {
26
- console.error.apply(console, Array.prototype.slice.call(arguments));
26
+ console.error.apply(console, arguments);
27
27
  }
28
28
  };
package/lib/options.js CHANGED
@@ -190,6 +190,12 @@ module.exports = optionator({
190
190
  default: false,
191
191
  description: "Automatically fix problems"
192
192
  },
193
+ {
194
+ option: "fix-dry-run",
195
+ type: "Boolean",
196
+ default: false,
197
+ description: "Automatically fix problems without saving the changes to the file system"
198
+ },
193
199
  {
194
200
  option: "debug",
195
201
  type: "Boolean",
@@ -214,6 +220,12 @@ module.exports = optionator({
214
220
  default: "true",
215
221
  description: "Prevent comments from changing config or rules"
216
222
  },
223
+ {
224
+ option: "report-unused-disable-directives",
225
+ type: "Boolean",
226
+ default: false,
227
+ description: "Adds reported errors for unused eslint-disable directives"
228
+ },
217
229
  {
218
230
  option: "print-config",
219
231
  type: "path::String",
@@ -23,7 +23,7 @@ module.exports = {
23
23
  {
24
24
  oneOf: [
25
25
  {
26
- enum: ["always", "never"]
26
+ enum: ["always", "never", "consistent"]
27
27
  },
28
28
  {
29
29
  type: "object",
@@ -58,11 +58,15 @@ module.exports = {
58
58
  * @returns {{multiline: boolean, minItems: number}} Normalized option object.
59
59
  */
60
60
  function normalizeOptionValue(option) {
61
+ let consistent = false;
61
62
  let multiline = false;
62
63
  let minItems = 0;
63
64
 
64
65
  if (option) {
65
- if (option === "always" || option.minItems === 0) {
66
+ if (option === "consistent") {
67
+ consistent = true;
68
+ minItems = Number.POSITIVE_INFINITY;
69
+ } else if (option === "always" || option.minItems === 0) {
66
70
  minItems = 0;
67
71
  } else if (option === "never") {
68
72
  minItems = Number.POSITIVE_INFINITY;
@@ -71,11 +75,12 @@ module.exports = {
71
75
  minItems = option.minItems || Number.POSITIVE_INFINITY;
72
76
  }
73
77
  } else {
78
+ consistent = false;
74
79
  multiline = true;
75
80
  minItems = Number.POSITIVE_INFINITY;
76
81
  }
77
82
 
78
- return { multiline, minItems };
83
+ return { consistent, multiline, minItems };
79
84
  }
80
85
 
81
86
  /**
@@ -173,8 +178,7 @@ module.exports = {
173
178
  /**
174
179
  * Reports a given node if it violated this rule.
175
180
  *
176
- * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
177
- * @param {{multiline: boolean, minItems: number}} options - An option object.
181
+ * @param {ASTNode} node - A node to check. This is an ArrayExpression node or an ArrayPattern node.
178
182
  * @returns {void}
179
183
  */
180
184
  function check(node) {
@@ -194,6 +198,16 @@ module.exports = {
194
198
  options.multiline &&
195
199
  elements.length > 0 &&
196
200
  firstIncComment.loc.start.line !== lastIncComment.loc.end.line
201
+ ) ||
202
+ (
203
+ elements.length === 0 &&
204
+ firstIncComment.type === "Block" &&
205
+ firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
206
+ firstIncComment === lastIncComment
207
+ ) ||
208
+ (
209
+ options.consistent &&
210
+ firstIncComment.loc.start.line !== openBracket.loc.end.line
197
211
  )
198
212
  );
199
213
 
@@ -14,7 +14,7 @@ const util = require("../ast-utils");
14
14
  module.exports = {
15
15
  meta: {
16
16
  docs: {
17
- description: "enforce consistent spacing inside single-line blocks",
17
+ description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
18
18
  category: "Stylistic Issues",
19
19
  recommended: false
20
20
  },
@@ -60,7 +60,8 @@ module.exports = {
60
60
  if (node.type === "MemberExpression") {
61
61
  if (node.object.type === "Identifier") {
62
62
  return true;
63
- } else if (node.object.type === "MemberExpression") {
63
+ }
64
+ if (node.object.type === "MemberExpression") {
64
65
  return containsOnlyIdentifiers(node.object);
65
66
  }
66
67
  }
@@ -247,7 +247,8 @@ module.exports = {
247
247
 
248
248
  if (capitalize === "always" && isLowercase) {
249
249
  return false;
250
- } else if (capitalize === "never" && isUppercase) {
250
+ }
251
+ if (capitalize === "never" && isUppercase) {
251
252
  return false;
252
253
  }
253
254
 
@@ -208,7 +208,9 @@ module.exports = {
208
208
  if (item) {
209
209
  const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
210
210
 
211
- previousItemToken = tokenAfterItem ? sourceCode.getTokenBefore(tokenAfterItem) : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
211
+ previousItemToken = tokenAfterItem
212
+ ? sourceCode.getTokenBefore(tokenAfterItem)
213
+ : sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
212
214
  }
213
215
  });
214
216
 
@@ -54,46 +54,67 @@ module.exports = {
54
54
  allowPattern = new RegExp(options.allowPattern);
55
55
  }
56
56
 
57
+ /**
58
+ * Check if the property is valid dot notation
59
+ * @param {ASTNode} node The dot notation node
60
+ * @param {string} value Value which is to be checked
61
+ * @returns {void}
62
+ */
63
+ function checkComputedProperty(node, value) {
64
+ if (
65
+ validIdentifier.test(value) &&
66
+ (allowKeywords || keywords.indexOf(String(value)) === -1) &&
67
+ !(allowPattern && allowPattern.test(value))
68
+ ) {
69
+ const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
70
+
71
+ context.report({
72
+ node: node.property,
73
+ message: "[{{propertyValue}}] is better written in dot notation.",
74
+ data: {
75
+ propertyValue: formattedValue
76
+ },
77
+ fix(fixer) {
78
+ const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
79
+ const rightBracket = sourceCode.getLastToken(node);
80
+
81
+ if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
82
+
83
+ // Don't perform any fixes if there are comments inside the brackets.
84
+ return null;
85
+ }
86
+
87
+ const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
88
+ const needsSpaceAfterProperty = tokenAfterProperty &&
89
+ rightBracket.range[1] === tokenAfterProperty.range[0] &&
90
+ !astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
91
+
92
+ const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
93
+ const textAfterProperty = needsSpaceAfterProperty ? " " : "";
94
+
95
+ return fixer.replaceTextRange(
96
+ [leftBracket.range[0], rightBracket.range[1]],
97
+ `${textBeforeDot}.${value}${textAfterProperty}`
98
+ );
99
+ }
100
+ });
101
+ }
102
+ }
103
+
57
104
  return {
58
105
  MemberExpression(node) {
59
106
  if (
60
107
  node.computed &&
61
- node.property.type === "Literal" &&
62
- validIdentifier.test(node.property.value) &&
63
- (allowKeywords || keywords.indexOf(String(node.property.value)) === -1)
108
+ node.property.type === "Literal"
64
109
  ) {
65
- if (!(allowPattern && allowPattern.test(node.property.value))) {
66
- context.report({
67
- node: node.property,
68
- message: "[{{propertyValue}}] is better written in dot notation.",
69
- data: {
70
- propertyValue: JSON.stringify(node.property.value)
71
- },
72
- fix(fixer) {
73
- const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
74
- const rightBracket = sourceCode.getLastToken(node);
75
-
76
- if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
77
-
78
- // Don't perform any fixes if there are comments inside the brackets.
79
- return null;
80
- }
81
-
82
- const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
83
- const needsSpaceAfterProperty = tokenAfterProperty &&
84
- rightBracket.range[1] === tokenAfterProperty.range[0] &&
85
- !astUtils.canTokensBeAdjacent(String(node.property.value), tokenAfterProperty);
86
-
87
- const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
88
- const textAfterProperty = needsSpaceAfterProperty ? " " : "";
89
-
90
- return fixer.replaceTextRange(
91
- [leftBracket.range[0], rightBracket.range[1]],
92
- `${textBeforeDot}.${node.property.value}${textAfterProperty}`
93
- );
94
- }
95
- });
96
- }
110
+ checkComputedProperty(node, node.property.value);
111
+ }
112
+ if (
113
+ node.computed &&
114
+ node.property.type === "TemplateLiteral" &&
115
+ node.property.expressions.length === 0
116
+ ) {
117
+ checkComputedProperty(node, node.property.quasis[0].value.cooked);
97
118
  }
98
119
  if (
99
120
  !allowKeywords &&
@@ -68,7 +68,7 @@ module.exports = {
68
68
 
69
69
  /**
70
70
  * Returns resolved option definitions based on an option and defaults
71
- *
71
+ *
72
72
  * @param {any} option - The option object or string value
73
73
  * @param {Object} defaults - The defaults to use if options are not present
74
74
  * @returns {Object} the resolved object definition
@@ -121,7 +121,7 @@ module.exports = {
121
121
 
122
122
  /**
123
123
  * Checks the spacing between two tokens before or after the star token.
124
- *
124
+ *
125
125
  * @param {string} kind Either "named", "anonymous", or "method"
126
126
  * @param {string} side Either "before" or "after".
127
127
  * @param {Token} leftToken `function` keyword token if side is "before", or
@@ -161,7 +161,7 @@ module.exports = {
161
161
 
162
162
  /**
163
163
  * Enforces the spacing around the star if node is a generator function.
164
- *
164
+ *
165
165
  * @param {ASTNode} node A function expression or declaration node.
166
166
  * @returns {void}
167
167
  */
@@ -733,7 +733,9 @@ module.exports = {
733
733
  } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
734
734
  const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
735
735
 
736
- if (parentElements[0] && parentElements[0].loc.start.line === parent.loc.start.line && parentElements[0].loc.end.line !== parent.loc.start.line) {
736
+ if (parentElements[0] &&
737
+ parentElements[0].loc.start.line === parent.loc.start.line &&
738
+ parentElements[0].loc.end.line !== parent.loc.start.line) {
737
739
 
738
740
  /*
739
741
  * If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
@@ -797,7 +799,8 @@ module.exports = {
797
799
  }
798
800
  }
799
801
 
800
- checkLastNodeLineIndent(node, nodeIndent + (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
802
+ checkLastNodeLineIndent(node, nodeIndent +
803
+ (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
801
804
  }
802
805
 
803
806
  /**