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.
- package/CHANGELOG.md +69 -0
- package/README.md +68 -156
- package/lib/built-in-rules-index.js +1 -0
- package/lib/cli-engine.js +5 -2
- package/lib/config/config-file.js +27 -4
- package/lib/config/config-initializer.js +151 -144
- package/lib/config/config-ops.js +2 -0
- package/lib/config/config-validator.js +1 -1
- package/lib/config/plugins.js +1 -1
- package/lib/formatters/codeframe.js +1 -1
- package/lib/formatters/stylish.js +2 -2
- package/lib/linter.js +7 -4
- package/lib/rules/accessor-pairs.js +4 -2
- package/lib/rules/array-callback-return.js +4 -3
- package/lib/rules/arrow-body-style.js +1 -1
- package/lib/rules/arrow-parens.js +2 -1
- package/lib/rules/arrow-spacing.js +7 -6
- package/lib/rules/brace-style.js +2 -1
- package/lib/rules/camelcase.js +5 -4
- package/lib/rules/capitalized-comments.js +10 -14
- package/lib/rules/class-methods-use-this.js +1 -1
- package/lib/rules/comma-spacing.js +10 -4
- package/lib/rules/complexity.js +6 -7
- package/lib/rules/consistent-return.js +2 -1
- package/lib/rules/curly.js +1 -1
- package/lib/rules/default-case.js +2 -2
- package/lib/rules/dot-notation.js +7 -5
- package/lib/rules/eol-last.js +1 -1
- package/lib/rules/func-call-spacing.js +2 -2
- package/lib/rules/func-style.js +3 -2
- package/lib/rules/getter-return.js +3 -2
- package/lib/rules/handle-callback-err.js +1 -1
- package/lib/rules/id-length.js +3 -2
- package/lib/rules/id-match.js +7 -4
- package/lib/rules/indent-legacy.js +1 -1
- package/lib/rules/indent.js +9 -6
- package/lib/rules/jsx-quotes.js +2 -2
- package/lib/rules/key-spacing.js +1 -1
- package/lib/rules/keyword-spacing.js +14 -14
- package/lib/rules/line-comment-position.js +5 -4
- package/lib/rules/lines-around-comment.js +14 -14
- package/lib/rules/lines-between-class-members.js +2 -1
- package/lib/rules/max-depth.js +5 -5
- package/lib/rules/max-len.js +21 -14
- package/lib/rules/max-lines-per-function.js +13 -17
- package/lib/rules/max-lines.js +3 -5
- package/lib/rules/max-nested-callbacks.js +6 -7
- package/lib/rules/max-params.js +5 -5
- package/lib/rules/max-statements-per-line.js +3 -2
- package/lib/rules/max-statements.js +6 -7
- package/lib/rules/multiline-comment-style.js +9 -9
- package/lib/rules/new-cap.js +9 -6
- package/lib/rules/newline-per-chained-call.js +2 -1
- package/lib/rules/no-alert.js +1 -1
- package/lib/rules/no-bitwise.js +2 -1
- package/lib/rules/no-caller.js +1 -1
- package/lib/rules/no-confusing-arrow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -1
- package/lib/rules/no-dupe-keys.js +2 -2
- package/lib/rules/no-duplicate-imports.js +2 -1
- package/lib/rules/no-else-return.js +4 -3
- package/lib/rules/no-empty-character-class.js +1 -1
- package/lib/rules/no-empty.js +2 -1
- package/lib/rules/no-eval.js +1 -1
- package/lib/rules/no-extra-parens.js +7 -1
- package/lib/rules/no-fallthrough.js +4 -3
- package/lib/rules/no-implicit-coercion.js +10 -7
- package/lib/rules/no-implied-eval.js +1 -1
- package/lib/rules/no-invalid-regexp.js +2 -2
- package/lib/rules/no-irregular-whitespace.js +11 -7
- package/lib/rules/no-labels.js +6 -4
- package/lib/rules/no-lonely-if.js +1 -1
- package/lib/rules/no-magic-numbers.js +6 -3
- package/lib/rules/no-mixed-operators.js +6 -5
- package/lib/rules/no-mixed-requires.js +1 -1
- package/lib/rules/no-mixed-spaces-and-tabs.js +2 -2
- package/lib/rules/no-multi-spaces.js +2 -1
- package/lib/rules/no-octal-escape.js +1 -1
- package/lib/rules/no-octal.js +1 -1
- package/lib/rules/no-param-reassign.js +2 -2
- package/lib/rules/no-path-concat.js +1 -1
- package/lib/rules/no-plusplus.js +2 -1
- package/lib/rules/no-redeclare.js +2 -2
- package/lib/rules/no-regex-spaces.js +1 -1
- package/lib/rules/no-return-assign.js +1 -1
- package/lib/rules/no-self-assign.js +3 -2
- package/lib/rules/no-shadow-restricted-names.js +16 -2
- package/lib/rules/no-shadow.js +3 -3
- package/lib/rules/no-sync.js +2 -1
- package/lib/rules/no-tabs.js +4 -3
- package/lib/rules/no-template-curly-in-string.js +1 -1
- package/lib/rules/no-trailing-spaces.js +7 -5
- package/lib/rules/no-undef.js +2 -1
- package/lib/rules/no-underscore-dangle.js +6 -3
- package/lib/rules/no-unexpected-multiline.js +1 -1
- package/lib/rules/no-unmodified-loop-condition.js +5 -5
- package/lib/rules/no-unneeded-ternary.js +2 -1
- package/lib/rules/no-unsafe-finally.js +3 -3
- package/lib/rules/no-unused-expressions.js +8 -5
- package/lib/rules/no-unused-vars.js +6 -6
- package/lib/rules/no-use-before-define.js +2 -2
- package/lib/rules/no-useless-escape.js +1 -1
- package/lib/rules/no-useless-rename.js +3 -3
- package/lib/rules/no-var.js +1 -1
- package/lib/rules/no-warning-comments.js +6 -6
- package/lib/rules/object-property-newline.js +5 -3
- package/lib/rules/one-var.js +18 -35
- package/lib/rules/padded-blocks.js +32 -9
- package/lib/rules/padding-line-between-statements.js +4 -3
- package/lib/rules/prefer-arrow-callback.js +4 -2
- package/lib/rules/prefer-const.js +5 -5
- package/lib/rules/prefer-destructuring.js +56 -5
- package/lib/rules/prefer-named-capture-group.js +123 -0
- package/lib/rules/prefer-object-spread.js +1 -1
- package/lib/rules/prefer-promise-reject-errors.js +1 -1
- package/lib/rules/prefer-spread.js +2 -13
- package/lib/rules/prefer-template.js +3 -3
- package/lib/rules/quotes.js +2 -2
- package/lib/rules/require-jsdoc.js +13 -7
- package/lib/rules/semi-spacing.js +6 -8
- package/lib/rules/semi.js +4 -4
- package/lib/rules/sort-imports.js +6 -3
- package/lib/rules/sort-keys.js +13 -5
- package/lib/rules/sort-vars.js +2 -1
- package/lib/rules/space-infix-ops.js +2 -1
- package/lib/rules/space-unary-ops.js +5 -3
- package/lib/rules/spaced-comment.js +6 -5
- package/lib/rules/switch-colon-spacing.js +2 -2
- package/lib/rules/template-curly-spacing.js +2 -2
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/valid-jsdoc.js +12 -7
- package/lib/rules/valid-typeof.js +2 -1
- package/lib/rules/vars-on-top.js +1 -1
- package/lib/rules/wrap-iife.js +3 -2
- package/lib/rules/yoda.js +6 -4
- package/lib/util/ajv.js +1 -0
- package/lib/util/ast-utils.js +12 -12
- package/lib/util/config-comment-parser.js +4 -4
- package/lib/util/glob-utils.js +3 -3
- package/lib/util/ignored-paths.js +4 -4
- package/lib/util/interpolate.js +1 -1
- package/lib/util/naming.js +6 -6
- package/lib/util/node-event-generator.js +1 -1
- package/lib/util/path-utils.js +2 -2
- package/lib/util/patterns/letters.js +1 -1
- package/lib/util/source-code.js +2 -2
- package/lib/util/xml-escape.js +1 -1
- package/package.json +25 -23
package/lib/rules/no-var.js
CHANGED
@@ -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(/[-/\\$^*+?.()|[\]{}]/
|
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
|
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
|
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
|
-
(
|
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."
|
package/lib/rules/one-var.js
CHANGED
@@ -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
|
-
|
80
|
-
|
81
|
-
}
|
82
|
-
|
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
|
-
|
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
|
-
|
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 (
|
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
|
-
|
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
|
68
|
+
const typeOptions = context.options[0] || "always";
|
69
|
+
const exceptOptions = context.options[1] || {};
|
55
70
|
|
56
|
-
if (typeof
|
57
|
-
const shouldHavePadding =
|
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(
|
64
|
-
options.blocks =
|
78
|
+
if (Object.prototype.hasOwnProperty.call(typeOptions, "blocks")) {
|
79
|
+
options.blocks = typeOptions.blocks === "always";
|
65
80
|
}
|
66
|
-
if (Object.prototype.hasOwnProperty.call(
|
67
|
-
options.switches =
|
81
|
+
if (Object.prototype.hasOwnProperty.call(typeOptions, "switches")) {
|
82
|
+
options.switches = typeOptions.switches === "always";
|
68
83
|
}
|
69
|
-
if (Object.prototype.hasOwnProperty.call(
|
70
|
-
options.classes =
|
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({
|
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
|
+
};
|
@@ -59,7 +59,7 @@ module.exports = {
|
|
59
59
|
},
|
60
60
|
|
61
61
|
schema: [],
|
62
|
-
fixable:
|
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(/\\*(
|
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]}`, "
|
197
|
+
}).replace(new RegExp(`\\\\${currentNode.raw[0]}`, "gu"), currentNode.raw[0])}\``;
|
198
198
|
}
|
199
199
|
|
200
200
|
if (currentNode.type === "TemplateLiteral") {
|
package/lib/rules/quotes.js
CHANGED
@@ -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(/\\(
|
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
|
}
|