eslint 5.13.0 → 5.15.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 (148) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +68 -156
  3. package/lib/built-in-rules-index.js +1 -0
  4. package/lib/cli-engine.js +5 -2
  5. package/lib/config/config-file.js +27 -4
  6. package/lib/config/config-initializer.js +151 -144
  7. package/lib/config/config-ops.js +2 -0
  8. package/lib/config/config-validator.js +1 -1
  9. package/lib/config/plugins.js +1 -1
  10. package/lib/formatters/codeframe.js +1 -1
  11. package/lib/formatters/stylish.js +2 -2
  12. package/lib/linter.js +7 -4
  13. package/lib/rules/accessor-pairs.js +4 -2
  14. package/lib/rules/array-callback-return.js +4 -3
  15. package/lib/rules/arrow-body-style.js +1 -1
  16. package/lib/rules/arrow-parens.js +2 -1
  17. package/lib/rules/arrow-spacing.js +7 -6
  18. package/lib/rules/brace-style.js +2 -1
  19. package/lib/rules/camelcase.js +5 -4
  20. package/lib/rules/capitalized-comments.js +10 -14
  21. package/lib/rules/class-methods-use-this.js +1 -1
  22. package/lib/rules/comma-spacing.js +10 -4
  23. package/lib/rules/complexity.js +6 -7
  24. package/lib/rules/consistent-return.js +2 -1
  25. package/lib/rules/curly.js +1 -1
  26. package/lib/rules/default-case.js +2 -2
  27. package/lib/rules/dot-notation.js +7 -5
  28. package/lib/rules/eol-last.js +1 -1
  29. package/lib/rules/func-call-spacing.js +2 -2
  30. package/lib/rules/func-style.js +3 -2
  31. package/lib/rules/getter-return.js +3 -2
  32. package/lib/rules/handle-callback-err.js +1 -1
  33. package/lib/rules/id-length.js +3 -2
  34. package/lib/rules/id-match.js +7 -4
  35. package/lib/rules/indent-legacy.js +1 -1
  36. package/lib/rules/indent.js +9 -6
  37. package/lib/rules/jsx-quotes.js +2 -2
  38. package/lib/rules/key-spacing.js +1 -1
  39. package/lib/rules/keyword-spacing.js +14 -14
  40. package/lib/rules/line-comment-position.js +5 -4
  41. package/lib/rules/lines-around-comment.js +14 -14
  42. package/lib/rules/lines-between-class-members.js +2 -1
  43. package/lib/rules/max-depth.js +5 -5
  44. package/lib/rules/max-len.js +21 -14
  45. package/lib/rules/max-lines-per-function.js +13 -17
  46. package/lib/rules/max-lines.js +3 -5
  47. package/lib/rules/max-nested-callbacks.js +6 -7
  48. package/lib/rules/max-params.js +5 -5
  49. package/lib/rules/max-statements-per-line.js +3 -2
  50. package/lib/rules/max-statements.js +6 -7
  51. package/lib/rules/multiline-comment-style.js +9 -9
  52. package/lib/rules/new-cap.js +9 -6
  53. package/lib/rules/newline-per-chained-call.js +2 -1
  54. package/lib/rules/no-alert.js +1 -1
  55. package/lib/rules/no-bitwise.js +2 -1
  56. package/lib/rules/no-caller.js +1 -1
  57. package/lib/rules/no-confusing-arrow.js +1 -1
  58. package/lib/rules/no-constant-condition.js +2 -1
  59. package/lib/rules/no-dupe-keys.js +2 -2
  60. package/lib/rules/no-duplicate-imports.js +2 -1
  61. package/lib/rules/no-else-return.js +4 -3
  62. package/lib/rules/no-empty-character-class.js +1 -1
  63. package/lib/rules/no-empty.js +2 -1
  64. package/lib/rules/no-eval.js +1 -1
  65. package/lib/rules/no-extra-parens.js +7 -1
  66. package/lib/rules/no-fallthrough.js +4 -3
  67. package/lib/rules/no-implicit-coercion.js +10 -7
  68. package/lib/rules/no-implied-eval.js +1 -1
  69. package/lib/rules/no-invalid-regexp.js +2 -2
  70. package/lib/rules/no-irregular-whitespace.js +11 -7
  71. package/lib/rules/no-labels.js +6 -4
  72. package/lib/rules/no-lonely-if.js +1 -1
  73. package/lib/rules/no-magic-numbers.js +6 -3
  74. package/lib/rules/no-mixed-operators.js +6 -5
  75. package/lib/rules/no-mixed-requires.js +1 -1
  76. package/lib/rules/no-mixed-spaces-and-tabs.js +2 -2
  77. package/lib/rules/no-multi-spaces.js +2 -1
  78. package/lib/rules/no-octal-escape.js +1 -1
  79. package/lib/rules/no-octal.js +1 -1
  80. package/lib/rules/no-param-reassign.js +2 -2
  81. package/lib/rules/no-path-concat.js +1 -1
  82. package/lib/rules/no-plusplus.js +2 -1
  83. package/lib/rules/no-redeclare.js +2 -2
  84. package/lib/rules/no-regex-spaces.js +1 -1
  85. package/lib/rules/no-return-assign.js +1 -1
  86. package/lib/rules/no-self-assign.js +3 -2
  87. package/lib/rules/no-shadow-restricted-names.js +16 -2
  88. package/lib/rules/no-shadow.js +3 -3
  89. package/lib/rules/no-sync.js +2 -1
  90. package/lib/rules/no-tabs.js +4 -3
  91. package/lib/rules/no-template-curly-in-string.js +1 -1
  92. package/lib/rules/no-trailing-spaces.js +7 -5
  93. package/lib/rules/no-undef.js +2 -1
  94. package/lib/rules/no-underscore-dangle.js +6 -3
  95. package/lib/rules/no-unexpected-multiline.js +1 -1
  96. package/lib/rules/no-unmodified-loop-condition.js +5 -5
  97. package/lib/rules/no-unneeded-ternary.js +2 -1
  98. package/lib/rules/no-unsafe-finally.js +3 -3
  99. package/lib/rules/no-unused-expressions.js +8 -5
  100. package/lib/rules/no-unused-vars.js +6 -6
  101. package/lib/rules/no-use-before-define.js +2 -2
  102. package/lib/rules/no-useless-escape.js +1 -1
  103. package/lib/rules/no-useless-rename.js +3 -3
  104. package/lib/rules/no-var.js +1 -1
  105. package/lib/rules/no-warning-comments.js +6 -6
  106. package/lib/rules/object-property-newline.js +5 -3
  107. package/lib/rules/one-var.js +18 -35
  108. package/lib/rules/padded-blocks.js +32 -9
  109. package/lib/rules/padding-line-between-statements.js +4 -3
  110. package/lib/rules/prefer-arrow-callback.js +4 -2
  111. package/lib/rules/prefer-const.js +5 -5
  112. package/lib/rules/prefer-destructuring.js +56 -5
  113. package/lib/rules/prefer-named-capture-group.js +123 -0
  114. package/lib/rules/prefer-object-spread.js +1 -1
  115. package/lib/rules/prefer-promise-reject-errors.js +1 -1
  116. package/lib/rules/prefer-spread.js +2 -13
  117. package/lib/rules/prefer-template.js +3 -3
  118. package/lib/rules/quotes.js +2 -2
  119. package/lib/rules/require-jsdoc.js +13 -7
  120. package/lib/rules/semi-spacing.js +6 -8
  121. package/lib/rules/semi.js +4 -4
  122. package/lib/rules/sort-imports.js +6 -3
  123. package/lib/rules/sort-keys.js +13 -5
  124. package/lib/rules/sort-vars.js +2 -1
  125. package/lib/rules/space-infix-ops.js +2 -1
  126. package/lib/rules/space-unary-ops.js +5 -3
  127. package/lib/rules/spaced-comment.js +6 -5
  128. package/lib/rules/switch-colon-spacing.js +2 -2
  129. package/lib/rules/template-curly-spacing.js +2 -2
  130. package/lib/rules/use-isnan.js +1 -1
  131. package/lib/rules/valid-jsdoc.js +12 -7
  132. package/lib/rules/valid-typeof.js +2 -1
  133. package/lib/rules/vars-on-top.js +1 -1
  134. package/lib/rules/wrap-iife.js +3 -2
  135. package/lib/rules/yoda.js +6 -4
  136. package/lib/util/ajv.js +1 -0
  137. package/lib/util/ast-utils.js +12 -12
  138. package/lib/util/config-comment-parser.js +4 -4
  139. package/lib/util/glob-utils.js +3 -3
  140. package/lib/util/ignored-paths.js +4 -4
  141. package/lib/util/interpolate.js +1 -1
  142. package/lib/util/naming.js +6 -6
  143. package/lib/util/node-event-generator.js +1 -1
  144. package/lib/util/path-utils.js +2 -2
  145. package/lib/util/patterns/letters.js +1 -1
  146. package/lib/util/source-code.js +2 -2
  147. package/lib/util/xml-escape.js +1 -1
  148. package/package.json +25 -23
@@ -77,7 +77,7 @@ function isDeclarationInitialized(node) {
77
77
  return node.declarations.every(declarator => declarator.init !== null);
78
78
  }
79
79
 
80
- const SCOPE_NODE_TYPE = /^(?:Program|BlockStatement|SwitchStatement|ForStatement|ForInStatement|ForOfStatement)$/;
80
+ const SCOPE_NODE_TYPE = /^(?:Program|BlockStatement|SwitchStatement|ForStatement|ForInStatement|ForOfStatement)$/u;
81
81
 
82
82
  /**
83
83
  * Gets the scope node which directly contains a given node.
@@ -47,7 +47,7 @@ module.exports = {
47
47
  configuration = context.options[0] || {},
48
48
  warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
49
49
  location = configuration.location || "start",
50
- selfConfigRegEx = /\bno-warning-comments\b/;
50
+ selfConfigRegEx = /\bno-warning-comments\b/u;
51
51
 
52
52
  /**
53
53
  * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified
@@ -58,7 +58,7 @@ module.exports = {
58
58
  * @returns {RegExp} The term converted to a RegExp
59
59
  */
60
60
  function convertToRegExp(term) {
61
- const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/g, "\\$&");
61
+ const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/gu, "\\$&");
62
62
  const wordBoundary = "\\b";
63
63
  const eitherOrWordBoundary = `|${wordBoundary}`;
64
64
  let prefix;
@@ -73,7 +73,7 @@ module.exports = {
73
73
  * In these cases, use no bounding match. Same applies for the
74
74
  * prefix, handled below.
75
75
  */
76
- const suffix = /\w$/.test(term) ? "\\b" : "";
76
+ const suffix = /\w$/u.test(term) ? "\\b" : "";
77
77
 
78
78
  if (location === "start") {
79
79
 
@@ -82,7 +82,7 @@ module.exports = {
82
82
  * there's no need to worry about word boundaries.
83
83
  */
84
84
  prefix = "^\\s*";
85
- } else if (/^\w/.test(term)) {
85
+ } else if (/^\w/u.test(term)) {
86
86
  prefix = wordBoundary;
87
87
  } else {
88
88
  prefix = "";
@@ -95,7 +95,7 @@ module.exports = {
95
95
  * ^\s*TERM\b. This checks the word boundary
96
96
  * at the beginning of the comment.
97
97
  */
98
- return new RegExp(prefix + escaped + suffix, "i");
98
+ return new RegExp(prefix + escaped + suffix, "i"); // eslint-disable-line require-unicode-regexp
99
99
  }
100
100
 
101
101
  /*
@@ -103,7 +103,7 @@ module.exports = {
103
103
  * \bTERM\b|\bTERM\b, this checks the entire comment
104
104
  * for the term.
105
105
  */
106
- return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i");
106
+ return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "i"); // eslint-disable-line require-unicode-regexp
107
107
  }
108
108
 
109
109
  const warningRegExps = warningTerms.map(convertToRegExp);
@@ -25,10 +25,12 @@ module.exports = {
25
25
  type: "object",
26
26
  properties: {
27
27
  allowAllPropertiesOnSameLine: {
28
- type: "boolean"
28
+ type: "boolean",
29
+ default: false
29
30
  },
30
31
  allowMultiplePropertiesPerLine: { // Deprecated
31
- type: "boolean"
32
+ type: "boolean",
33
+ default: false
32
34
  }
33
35
  },
34
36
  additionalProperties: false
@@ -40,7 +42,7 @@ module.exports = {
40
42
 
41
43
  create(context) {
42
44
  const allowSameLine = context.options[0] && (
43
- (Boolean(context.options[0].allowAllPropertiesOnSameLine) || Boolean(context.options[0].allowMultiplePropertiesPerLine)) // Deprecated
45
+ (context.options[0].allowAllPropertiesOnSameLine || context.options[0].allowMultiplePropertiesPerLine /* Deprecated */)
44
46
  );
45
47
  const errorMessage = allowSameLine
46
48
  ? "Object properties must go on a new line if they aren't all on the same line."
@@ -32,7 +32,8 @@ module.exports = {
32
32
  type: "object",
33
33
  properties: {
34
34
  separateRequires: {
35
- type: "boolean"
35
+ type: "boolean",
36
+ default: false
36
37
  },
37
38
  var: {
38
39
  enum: ["always", "never", "consecutive"]
@@ -76,42 +77,16 @@ module.exports = {
76
77
  options.let = { uninitialized: mode, initialized: mode };
77
78
  options.const = { uninitialized: mode, initialized: mode };
78
79
  } else if (typeof mode === "object") { // options configuration is an object
79
- if (Object.prototype.hasOwnProperty.call(mode, "separateRequires")) {
80
- options.separateRequires = !!mode.separateRequires;
81
- }
82
- if (Object.prototype.hasOwnProperty.call(mode, "var")) {
83
- options.var = { uninitialized: mode.var, initialized: mode.var };
84
- }
85
- if (Object.prototype.hasOwnProperty.call(mode, "let")) {
86
- options.let = { uninitialized: mode.let, initialized: mode.let };
87
- }
88
- if (Object.prototype.hasOwnProperty.call(mode, "const")) {
89
- options.const = { uninitialized: mode.const, initialized: mode.const };
90
- }
80
+ options.separateRequires = mode.separateRequires;
81
+ options.var = { uninitialized: mode.var, initialized: mode.var };
82
+ options.let = { uninitialized: mode.let, initialized: mode.let };
83
+ options.const = { uninitialized: mode.const, initialized: mode.const };
91
84
  if (Object.prototype.hasOwnProperty.call(mode, "uninitialized")) {
92
- if (!options.var) {
93
- options.var = {};
94
- }
95
- if (!options.let) {
96
- options.let = {};
97
- }
98
- if (!options.const) {
99
- options.const = {};
100
- }
101
85
  options.var.uninitialized = mode.uninitialized;
102
86
  options.let.uninitialized = mode.uninitialized;
103
87
  options.const.uninitialized = mode.uninitialized;
104
88
  }
105
89
  if (Object.prototype.hasOwnProperty.call(mode, "initialized")) {
106
- if (!options.var) {
107
- options.var = {};
108
- }
109
- if (!options.let) {
110
- options.let = {};
111
- }
112
- if (!options.const) {
113
- options.const = {};
114
- }
115
90
  options.var.initialized = mode.initialized;
116
91
  options.let.initialized = mode.initialized;
117
92
  options.const.initialized = mode.initialized;
@@ -257,7 +232,9 @@ module.exports = {
257
232
 
258
233
  if (currentOptions.uninitialized === MODE_ALWAYS && currentOptions.initialized === MODE_ALWAYS) {
259
234
  if (currentScope.uninitialized || currentScope.initialized) {
260
- return false;
235
+ if (!hasRequires) {
236
+ return false;
237
+ }
261
238
  }
262
239
  }
263
240
 
@@ -268,7 +245,9 @@ module.exports = {
268
245
  }
269
246
  if (declarationCounts.initialized > 0) {
270
247
  if (currentOptions.initialized === MODE_ALWAYS && currentScope.initialized) {
271
- return false;
248
+ if (!hasRequires) {
249
+ return false;
250
+ }
272
251
  }
273
252
  }
274
253
  if (currentScope.required && hasRequires) {
@@ -340,7 +319,11 @@ module.exports = {
340
319
  * y`
341
320
  * ^ afterComma
342
321
  */
343
- if (afterComma.loc.start.line > tokenAfterDeclarator.loc.end.line || afterComma.type === "Line" || afterComma.type === "Block") {
322
+ if (
323
+ afterComma.loc.start.line > tokenAfterDeclarator.loc.end.line ||
324
+ afterComma.type === "Line" ||
325
+ afterComma.type === "Block"
326
+ ) {
344
327
  let lastComment = afterComma;
345
328
 
346
329
  while (lastComment.type === "Line" || lastComment.type === "Block") {
@@ -349,7 +332,7 @@ module.exports = {
349
332
 
350
333
  return fixer.replaceTextRange(
351
334
  [tokenAfterDeclarator.range[0], lastComment.range[0]],
352
- `;\n${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}\n${declaration.kind} `
335
+ `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${declaration.kind} `
353
336
  );
354
337
  }
355
338
 
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../util/ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -45,32 +51,45 @@ module.exports = {
45
51
  minProperties: 1
46
52
  }
47
53
  ]
54
+ },
55
+ {
56
+ type: "object",
57
+ properties: {
58
+ allowSingleLineBlocks: {
59
+ type: "boolean"
60
+ }
61
+ }
48
62
  }
49
63
  ]
50
64
  },
51
65
 
52
66
  create(context) {
53
67
  const options = {};
54
- const config = context.options[0] || "always";
68
+ const typeOptions = context.options[0] || "always";
69
+ const exceptOptions = context.options[1] || {};
55
70
 
56
- if (typeof config === "string") {
57
- const shouldHavePadding = config === "always";
71
+ if (typeof typeOptions === "string") {
72
+ const shouldHavePadding = typeOptions === "always";
58
73
 
59
74
  options.blocks = shouldHavePadding;
60
75
  options.switches = shouldHavePadding;
61
76
  options.classes = shouldHavePadding;
62
77
  } else {
63
- if (Object.prototype.hasOwnProperty.call(config, "blocks")) {
64
- options.blocks = config.blocks === "always";
78
+ if (Object.prototype.hasOwnProperty.call(typeOptions, "blocks")) {
79
+ options.blocks = typeOptions.blocks === "always";
65
80
  }
66
- if (Object.prototype.hasOwnProperty.call(config, "switches")) {
67
- options.switches = config.switches === "always";
81
+ if (Object.prototype.hasOwnProperty.call(typeOptions, "switches")) {
82
+ options.switches = typeOptions.switches === "always";
68
83
  }
69
- if (Object.prototype.hasOwnProperty.call(config, "classes")) {
70
- options.classes = config.classes === "always";
84
+ if (Object.prototype.hasOwnProperty.call(typeOptions, "classes")) {
85
+ options.classes = typeOptions.classes === "always";
71
86
  }
72
87
  }
73
88
 
89
+ if (Object.prototype.hasOwnProperty.call(exceptOptions, "allowSingleLineBlocks")) {
90
+ options.allowSingleLineBlocks = exceptOptions.allowSingleLineBlocks === true;
91
+ }
92
+
74
93
  const ALWAYS_MESSAGE = "Block must be padded by blank lines.",
75
94
  NEVER_MESSAGE = "Block must not be padded by blank lines.";
76
95
 
@@ -177,6 +196,10 @@ module.exports = {
177
196
  blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
178
197
  blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
179
198
 
199
+ if (options.allowSingleLineBlocks && astUtils.isTokenOnSameLine(tokenBeforeFirst, tokenAfterLast)) {
200
+ return;
201
+ }
202
+
180
203
  if (requirePaddingFor(node)) {
181
204
  if (!blockHasTopPadding) {
182
205
  context.report({
@@ -17,10 +17,11 @@ const astUtils = require("../util/ast-utils");
17
17
 
18
18
  const LT = `[${Array.from(astUtils.LINEBREAKS).join("")}]`;
19
19
  const PADDING_LINE_SEQUENCE = new RegExp(
20
- String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`
20
+ String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`,
21
+ "u"
21
22
  );
22
- const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/;
23
- const CJS_IMPORT = /^require\(/;
23
+ const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
24
+ const CJS_IMPORT = /^require\(/u;
24
25
 
25
26
  /**
26
27
  * Creates tester which check if a node starts with specific keyword.
@@ -146,10 +146,12 @@ module.exports = {
146
146
  type: "object",
147
147
  properties: {
148
148
  allowNamedFunctions: {
149
- type: "boolean"
149
+ type: "boolean",
150
+ default: false
150
151
  },
151
152
  allowUnboundThis: {
152
- type: "boolean"
153
+ type: "boolean",
154
+ default: true
153
155
  }
154
156
  },
155
157
  additionalProperties: false
@@ -11,9 +11,9 @@ const astUtils = require("../util/ast-utils");
11
11
  // Helpers
12
12
  //------------------------------------------------------------------------------
13
13
 
14
- const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/;
15
- const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/;
16
- const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/;
14
+ const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
15
+ const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/u;
16
+ const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/u;
17
17
 
18
18
  /**
19
19
  * Checks whether a given node is located at `ForStatement.init` or not.
@@ -345,8 +345,8 @@ module.exports = {
345
345
  {
346
346
  type: "object",
347
347
  properties: {
348
- destructuring: { enum: ["any", "all"] },
349
- ignoreReadBeforeAssign: { type: "boolean" }
348
+ destructuring: { enum: ["any", "all"], default: "any" },
349
+ ignoreReadBeforeAssign: { type: "boolean", default: false }
350
350
  },
351
351
  additionalProperties: false
352
352
  }
@@ -19,6 +19,8 @@ module.exports = {
19
19
  url: "https://eslint.org/docs/rules/prefer-destructuring"
20
20
  },
21
21
 
22
+ fixable: "code",
23
+
22
24
  schema: [
23
25
  {
24
26
 
@@ -130,10 +132,55 @@ module.exports = {
130
132
  *
131
133
  * @param {ASTNode} reportNode the node to report
132
134
  * @param {string} type the type of destructuring that should have been done
135
+ * @param {Function|null} fix the fix function or null to pass to context.report
133
136
  * @returns {void}
134
137
  */
135
- function report(reportNode, type) {
136
- context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
138
+ function report(reportNode, type, fix) {
139
+ context.report({
140
+ node: reportNode,
141
+ message: "Use {{type}} destructuring.",
142
+ data: { type },
143
+ fix
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Determines if a node should be fixed into object destructuring
149
+ *
150
+ * The fixer only fixes the simplest case of object destructuring,
151
+ * like: `let x = a.x`;
152
+ *
153
+ * Assignment expression is not fixed.
154
+ * Array destructuring is not fixed.
155
+ * Renamed property is not fixed.
156
+ *
157
+ * @param {ASTNode} node the the node to evaluate
158
+ * @returns {boolean} whether or not the node should be fixed
159
+ */
160
+ function shouldFix(node) {
161
+ return node.type === "VariableDeclarator" &&
162
+ node.id.type === "Identifier" &&
163
+ node.init.type === "MemberExpression" &&
164
+ node.id.name === node.init.property.name;
165
+ }
166
+
167
+ /**
168
+ * Fix a node into object destructuring.
169
+ * This function only handles the simplest case of object destructuring,
170
+ * see {@link shouldFix}.
171
+ *
172
+ * @param {SourceCodeFixer} fixer the fixer object
173
+ * @param {ASTNode} node the node to be fixed.
174
+ * @returns {Object} a fix for the node
175
+ */
176
+ function fixIntoObjectDestructuring(fixer, node) {
177
+ const rightNode = node.init;
178
+ const sourceCode = context.getSourceCode();
179
+
180
+ return fixer.replaceText(
181
+ node,
182
+ `{${rightNode.property.name}} = ${sourceCode.getText(rightNode.object)}`
183
+ );
137
184
  }
138
185
 
139
186
  /**
@@ -155,13 +202,17 @@ module.exports = {
155
202
 
156
203
  if (isArrayIndexAccess(rightNode)) {
157
204
  if (shouldCheck(reportNode.type, "array")) {
158
- report(reportNode, "array");
205
+ report(reportNode, "array", null);
159
206
  }
160
207
  return;
161
208
  }
162
209
 
210
+ const fix = shouldFix(reportNode)
211
+ ? fixer => fixIntoObjectDestructuring(fixer, reportNode)
212
+ : null;
213
+
163
214
  if (shouldCheck(reportNode.type, "object") && enforceForRenamedProperties) {
164
- report(reportNode, "object");
215
+ report(reportNode, "object", fix);
165
216
  return;
166
217
  }
167
218
 
@@ -172,7 +223,7 @@ module.exports = {
172
223
  (property.type === "Literal" && leftNode.name === property.value) ||
173
224
  (property.type === "Identifier" && leftNode.name === property.name && !rightNode.computed)
174
225
  ) {
175
- report(reportNode, "object");
226
+ report(reportNode, "object", fix);
176
227
  }
177
228
  }
178
229
  }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @fileoverview Rule to enforce requiring named capture groups in regular expression.
3
+ * @author Pig Fang <https://github.com/g-plane>
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const {
13
+ CALL,
14
+ CONSTRUCT,
15
+ ReferenceTracker,
16
+ getStringIfConstant
17
+ } = require("eslint-utils");
18
+ const regexpp = require("regexpp");
19
+
20
+ //------------------------------------------------------------------------------
21
+ // Helpers
22
+ //------------------------------------------------------------------------------
23
+
24
+ const parser = new regexpp.RegExpParser();
25
+
26
+ //------------------------------------------------------------------------------
27
+ // Rule Definition
28
+ //------------------------------------------------------------------------------
29
+
30
+ module.exports = {
31
+ meta: {
32
+ type: "suggestion",
33
+
34
+ docs: {
35
+ description: "enforce using named capture group in regular expression",
36
+ category: "Best Practices",
37
+ recommended: false,
38
+ url: "https://eslint.org/docs/rules/prefer-named-capture-group"
39
+ },
40
+
41
+ schema: [],
42
+
43
+ messages: {
44
+ required: "Capture group '{{group}}' should be converted to a named or non-capturing group."
45
+ }
46
+ },
47
+
48
+ create(context) {
49
+
50
+ /**
51
+ * Function to check regular expression.
52
+ *
53
+ * @param {string} regex The regular expression to be check.
54
+ * @param {ASTNode} node AST node which contains regular expression.
55
+ * @param {boolean} uFlag Flag indicates whether unicode mode is enabled or not.
56
+ * @returns {void}
57
+ */
58
+ function checkRegex(regex, node, uFlag) {
59
+ let ast;
60
+
61
+ try {
62
+ ast = parser.parsePattern(regex, 0, regex.length, uFlag);
63
+ } catch (_) {
64
+
65
+ // ignore regex syntax errors
66
+ return;
67
+ }
68
+
69
+ regexpp.visitRegExpAST(ast, {
70
+ onCapturingGroupEnter(group) {
71
+ if (!group.name) {
72
+ const locNode = node.type === "Literal" ? node : node.arguments[0];
73
+
74
+ context.report({
75
+ node,
76
+ messageId: "required",
77
+ loc: {
78
+ start: {
79
+ line: locNode.loc.start.line,
80
+ column: locNode.loc.start.column + group.start + 1
81
+ },
82
+ end: {
83
+ line: locNode.loc.start.line,
84
+ column: locNode.loc.start.column + group.end + 1
85
+ }
86
+ },
87
+ data: {
88
+ group: group.raw
89
+ }
90
+ });
91
+ }
92
+ }
93
+ });
94
+ }
95
+
96
+ return {
97
+ Literal(node) {
98
+ if (node.regex) {
99
+ checkRegex(node.regex.pattern, node, node.regex.flags.includes("u"));
100
+ }
101
+ },
102
+ Program() {
103
+ const scope = context.getScope();
104
+ const tracker = new ReferenceTracker(scope);
105
+ const traceMap = {
106
+ RegExp: {
107
+ [CALL]: true,
108
+ [CONSTRUCT]: true
109
+ }
110
+ };
111
+
112
+ for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
113
+ const regex = getStringIfConstant(node.arguments[0]);
114
+ const flags = getStringIfConstant(node.arguments[1]);
115
+
116
+ if (regex) {
117
+ checkRegex(regex, node, flags && flags.includes("u"));
118
+ }
119
+ }
120
+ }
121
+ };
122
+ }
123
+ };
@@ -14,7 +14,7 @@ const {
14
14
  isParenthesised
15
15
  } = require("../util/ast-utils");
16
16
 
17
- const ANY_SPACE = /\s/;
17
+ const ANY_SPACE = /\s/u;
18
18
 
19
19
  /**
20
20
  * Helper that checks if the Object.assign call has array spread
@@ -27,7 +27,7 @@ module.exports = {
27
27
  {
28
28
  type: "object",
29
29
  properties: {
30
- allowEmptyReject: { type: "boolean" }
30
+ allowEmptyReject: { type: "boolean", default: false }
31
31
  },
32
32
  additionalProperties: false
33
33
  }
@@ -59,7 +59,7 @@ module.exports = {
59
59
  },
60
60
 
61
61
  schema: [],
62
- fixable: "code"
62
+ fixable: null
63
63
  },
64
64
 
65
65
  create(context) {
@@ -78,18 +78,7 @@ module.exports = {
78
78
  if (isValidThisArg(expectedThis, thisArg, sourceCode)) {
79
79
  context.report({
80
80
  node,
81
- message: "Use the spread operator instead of '.apply()'.",
82
- fix(fixer) {
83
- if (expectedThis && expectedThis.type !== "Identifier") {
84
-
85
- // Don't fix cases where the `this` value could be a computed expression.
86
- return null;
87
- }
88
-
89
- const propertyDot = sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === ".");
90
-
91
- return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`);
92
- }
81
+ message: "Use the spread operator instead of '.apply()'."
93
82
  });
94
83
  }
95
84
  }
@@ -52,7 +52,7 @@ function isOctalEscapeSequence(node) {
52
52
  return false;
53
53
  }
54
54
 
55
- const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7]{1,3})/);
55
+ const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7]{1,3})/u);
56
56
 
57
57
  if (match) {
58
58
 
@@ -187,14 +187,14 @@ module.exports = {
187
187
  * for some reason, don't add another backslash, because that would change the meaning of the code (it would cause
188
188
  * an actual backslash character to appear before the dollar sign).
189
189
  */
190
- return `\`${currentNode.raw.slice(1, -1).replace(/\\*(\${|`)/g, matched => {
190
+ return `\`${currentNode.raw.slice(1, -1).replace(/\\*(\$\{|`)/gu, matched => {
191
191
  if (matched.lastIndexOf("\\") % 2) {
192
192
  return `\\${matched}`;
193
193
  }
194
194
  return matched;
195
195
 
196
196
  // Unescape any quotes that appear in the original Literal that no longer need to be escaped.
197
- }).replace(new RegExp(`\\\\${currentNode.raw[0]}`, "g"), currentNode.raw[0])}\``;
197
+ }).replace(new RegExp(`\\\\${currentNode.raw[0]}`, "gu"), currentNode.raw[0])}\``;
198
198
  }
199
199
 
200
200
  if (currentNode.type === "TemplateLiteral") {
@@ -34,7 +34,7 @@ const QUOTE_SETTINGS = {
34
34
  };
35
35
 
36
36
  // An unescaped newline is a newline preceded by an even number of backslashes.
37
- const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`);
37
+ const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`, "u");
38
38
 
39
39
  /**
40
40
  * Switches quoting of javascript string between ' " and `
@@ -54,7 +54,7 @@ QUOTE_SETTINGS.backtick.convert = function(str) {
54
54
  if (newQuote === oldQuote) {
55
55
  return str;
56
56
  }
57
- return newQuote + str.slice(1, -1).replace(/\\(\${|\r\n?|\n|.)|["'`]|\${|(\r\n?|\n)/g, (match, escaped, newline) => {
57
+ return newQuote + str.slice(1, -1).replace(/\\(\$\{|\r\n?|\n|.)|["'`]|\$\{|(\r\n?|\n)/gu, (match, escaped, newline) => {
58
58
  if (escaped === oldQuote || oldQuote === "`" && escaped === "${") {
59
59
  return escaped; // unescape
60
60
  }