eslint-plugin-unicorn 43.0.2 → 44.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.
- package/configs/recommended.js +2 -0
- package/package.json +16 -15
- package/readme.md +6 -4
- package/rules/ast/is-empty-node.js +1 -1
- package/rules/ast/is-static-require.js +1 -3
- package/rules/better-regex.js +19 -2
- package/rules/consistent-function-scoping.js +5 -8
- package/rules/custom-error-definition.js +3 -3
- package/rules/explicit-length-check.js +0 -1
- package/rules/filename-case.js +2 -2
- package/rules/fix/add-parenthesizes-to-return-or-throw-expression.js +21 -0
- package/rules/fix/index.js +1 -0
- package/rules/fix/remove-spaces-after.js +4 -4
- package/rules/fix/switch-new-expression-to-call-expression.js +14 -34
- package/rules/new-for-builtins.js +49 -48
- package/rules/no-array-callback-reference.js +16 -0
- package/rules/no-array-for-each.js +26 -10
- package/rules/no-array-method-this-argument.js +2 -0
- package/rules/no-await-expression-member.js +2 -0
- package/rules/no-document-cookie.js +6 -22
- package/rules/no-empty-file.js +1 -1
- package/rules/no-for-loop.js +4 -4
- package/rules/no-keyword-prefix.js +3 -7
- package/rules/no-new-buffer.js +4 -4
- package/rules/no-thenable.js +2 -5
- package/rules/no-unnecessary-await.js +107 -0
- package/rules/no-unsafe-regex.js +2 -2
- package/rules/no-unused-properties.js +2 -4
- package/rules/no-useless-promise-resolve-reject.js +3 -3
- package/rules/no-useless-spread.js +11 -4
- package/rules/no-useless-undefined.js +1 -2
- package/rules/prefer-array-find.js +83 -6
- package/rules/prefer-array-index-of.js +15 -4
- package/rules/prefer-array-some.js +15 -14
- package/rules/prefer-at.js +19 -0
- package/rules/prefer-dom-node-dataset.js +2 -1
- package/rules/prefer-export-from.js +15 -7
- package/rules/prefer-json-parse-buffer.js +2 -4
- package/rules/prefer-keyboard-event-key.js +10 -18
- package/rules/prefer-logical-operator-over-ternary.js +8 -4
- package/rules/prefer-math-trunc.js +0 -4
- package/rules/prefer-module.js +1 -1
- package/rules/prefer-native-coercion-functions.js +4 -7
- package/rules/prefer-negative-index.js +3 -2
- package/rules/prefer-number-properties.js +49 -58
- package/rules/prefer-prototype-methods.js +2 -5
- package/rules/prefer-set-has.js +1 -10
- package/rules/prefer-string-slice.js +1 -1
- package/rules/prefer-string-starts-ends-with.js +10 -5
- package/rules/prefer-switch.js +9 -4
- package/rules/prefer-ternary.js +1 -1
- package/rules/prefer-type-error.js +20 -7
- package/rules/prevent-abbreviations.js +7 -1
- package/rules/relative-url-style.js +2 -4
- package/rules/require-post-message-target-origin.js +10 -18
- package/rules/selectors/matches-any.js +8 -3
- package/rules/selectors/prototype-method-selector.js +5 -2
- package/rules/string-content.js +5 -7
- package/rules/switch-case-braces.js +109 -0
- package/rules/template-indent.js +4 -2
- package/rules/text-encoding-identifier-case.js +9 -10
- package/rules/utils/assert-token.js +1 -1
- package/rules/utils/avoid-capture.js +1 -1
- package/rules/utils/boolean.js +1 -1
- package/rules/utils/global-reference-tracker.js +72 -0
- package/rules/utils/is-number.js +5 -5
- package/rules/utils/is-on-same-line.js +7 -0
- package/rules/utils/is-same-reference.js +16 -8
- package/rules/utils/is-shadowed.js +1 -2
- package/rules/utils/rule.js +20 -10
- package/rules/utils/should-add-parentheses-to-expression-statement-expression.js +8 -3
- package/rules/utils/should-add-parentheses-to-member-expression-object.js +8 -3
package/configs/recommended.js
CHANGED
|
@@ -50,6 +50,7 @@ module.exports = {
|
|
|
50
50
|
'unicorn/no-static-only-class': 'error',
|
|
51
51
|
'unicorn/no-thenable': 'error',
|
|
52
52
|
'unicorn/no-this-assignment': 'error',
|
|
53
|
+
'unicorn/no-unnecessary-await': 'error',
|
|
53
54
|
'unicorn/no-unreadable-array-destructuring': 'error',
|
|
54
55
|
'unicorn/no-unreadable-iife': 'error',
|
|
55
56
|
'unicorn/no-unsafe-regex': 'off',
|
|
@@ -118,6 +119,7 @@ module.exports = {
|
|
|
118
119
|
// See #1396
|
|
119
120
|
'unicorn/require-post-message-target-origin': 'off',
|
|
120
121
|
'unicorn/string-content': 'off',
|
|
122
|
+
'unicorn/switch-case-braces': 'error',
|
|
121
123
|
'unicorn/template-indent': 'error',
|
|
122
124
|
'unicorn/text-encoding-identifier-case': 'error',
|
|
123
125
|
'unicorn/throw-new-error': 'error',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-unicorn",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "44.0.0",
|
|
4
4
|
"description": "More than 100 powerful ESLint rules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/eslint-plugin-unicorn",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"xo"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@babel/helper-validator-identifier": "^7.
|
|
50
|
-
"ci-info": "^3.
|
|
49
|
+
"@babel/helper-validator-identifier": "^7.19.1",
|
|
50
|
+
"ci-info": "^3.4.0",
|
|
51
51
|
"clean-regexp": "^1.0.0",
|
|
52
52
|
"eslint-utils": "^3.0.0",
|
|
53
53
|
"esquery": "^1.4.0",
|
|
54
54
|
"indent-string": "^4.0.0",
|
|
55
|
-
"is-builtin-module": "^3.
|
|
55
|
+
"is-builtin-module": "^3.2.0",
|
|
56
56
|
"lodash": "^4.17.21",
|
|
57
57
|
"pluralize": "^8.0.0",
|
|
58
58
|
"read-pkg-up": "^7.0.1",
|
|
@@ -63,34 +63,35 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@babel/code-frame": "^7.18.6",
|
|
66
|
-
"@babel/core": "^7.
|
|
67
|
-
"@babel/eslint-parser": "^7.
|
|
66
|
+
"@babel/core": "^7.19.1",
|
|
67
|
+
"@babel/eslint-parser": "^7.19.1",
|
|
68
68
|
"@lubien/fixture-beta-package": "^1.0.0-beta.1",
|
|
69
|
-
"@typescript-eslint/parser": "^5.
|
|
69
|
+
"@typescript-eslint/parser": "^5.37.0",
|
|
70
70
|
"ava": "^3.15.0",
|
|
71
|
-
"c8": "^7.
|
|
71
|
+
"c8": "^7.12.0",
|
|
72
72
|
"chalk": "^5.0.1",
|
|
73
73
|
"enquirer": "^2.3.6",
|
|
74
|
-
"eslint": "^8.
|
|
74
|
+
"eslint": "^8.23.1",
|
|
75
75
|
"eslint-ava-rule-tester": "^4.0.0",
|
|
76
|
-
"eslint-plugin-eslint-plugin": "^5.0.
|
|
76
|
+
"eslint-plugin-eslint-plugin": "^5.0.6",
|
|
77
77
|
"eslint-plugin-internal-rules": "file:./scripts/internal-rules/",
|
|
78
78
|
"eslint-remote-tester": "^3.0.0",
|
|
79
79
|
"eslint-remote-tester-repositories": "^0.0.6",
|
|
80
80
|
"execa": "^6.1.0",
|
|
81
81
|
"listr": "^0.14.3",
|
|
82
82
|
"lodash-es": "^4.17.21",
|
|
83
|
-
"markdownlint-cli": "^0.
|
|
83
|
+
"markdownlint-cli": "^0.32.2",
|
|
84
84
|
"mem": "^9.0.2",
|
|
85
85
|
"npm-package-json-lint": "^6.3.0",
|
|
86
86
|
"npm-run-all": "^4.1.5",
|
|
87
87
|
"outdent": "^0.8.0",
|
|
88
|
-
"typescript": "^4.
|
|
89
|
-
"vue-eslint-parser": "^9.0
|
|
90
|
-
"xo": "^0.
|
|
88
|
+
"typescript": "^4.8.3",
|
|
89
|
+
"vue-eslint-parser": "^9.1.0",
|
|
90
|
+
"xo": "^0.52.3",
|
|
91
|
+
"yaml": "^1.10.2"
|
|
91
92
|
},
|
|
92
93
|
"peerDependencies": {
|
|
93
|
-
"eslint": ">=8.
|
|
94
|
+
"eslint": ">=8.23.1"
|
|
94
95
|
},
|
|
95
96
|
"ava": {
|
|
96
97
|
"files": [
|
package/readme.md
CHANGED
|
@@ -91,6 +91,7 @@ Each rule has emojis denoting:
|
|
|
91
91
|
| [no-static-only-class](docs/rules/no-static-only-class.md) | Disallow classes that only have static members. | ✅ | 🔧 | |
|
|
92
92
|
| [no-thenable](docs/rules/no-thenable.md) | Disallow `then` property. | ✅ | | |
|
|
93
93
|
| [no-this-assignment](docs/rules/no-this-assignment.md) | Disallow assigning `this` to a variable. | ✅ | | |
|
|
94
|
+
| [no-unnecessary-await](docs/rules/no-unnecessary-await.md) | Disallow awaiting non-promise values. | ✅ | 🔧 | |
|
|
94
95
|
| [no-unreadable-array-destructuring](docs/rules/no-unreadable-array-destructuring.md) | Disallow unreadable array destructuring. | ✅ | 🔧 | |
|
|
95
96
|
| [no-unreadable-iife](docs/rules/no-unreadable-iife.md) | Disallow unreadable IIFEs. | ✅ | | |
|
|
96
97
|
| [no-unsafe-regex](docs/rules/no-unsafe-regex.md) | Disallow unsafe regular expressions. | | | |
|
|
@@ -105,11 +106,11 @@ Each rule has emojis denoting:
|
|
|
105
106
|
| [number-literal-case](docs/rules/number-literal-case.md) | Enforce proper case for numeric literals. | ✅ | 🔧 | |
|
|
106
107
|
| [numeric-separators-style](docs/rules/numeric-separators-style.md) | Enforce the style of numeric separators by correctly grouping digits. | ✅ | 🔧 | |
|
|
107
108
|
| [prefer-add-event-listener](docs/rules/prefer-add-event-listener.md) | Prefer `.addEventListener()` and `.removeEventListener()` over `on`-functions. | ✅ | 🔧 | |
|
|
108
|
-
| [prefer-array-find](docs/rules/prefer-array-find.md) | Prefer `.find(…)` over the first element from `.filter(…)`. | ✅ | 🔧 | 💡 |
|
|
109
|
+
| [prefer-array-find](docs/rules/prefer-array-find.md) | Prefer `.find(…)` and `.findLast(…)` over the first or last element from `.filter(…)`. | ✅ | 🔧 | 💡 |
|
|
109
110
|
| [prefer-array-flat](docs/rules/prefer-array-flat.md) | Prefer `Array#flat()` over legacy techniques to flatten arrays. | ✅ | 🔧 | |
|
|
110
111
|
| [prefer-array-flat-map](docs/rules/prefer-array-flat-map.md) | Prefer `.flatMap(…)` over `.map(…).flat()`. | ✅ | 🔧 | |
|
|
111
|
-
| [prefer-array-index-of](docs/rules/prefer-array-index-of.md) | Prefer `Array#indexOf()` over `Array#findIndex()` when looking for the index of an item. | ✅ | 🔧 | 💡 |
|
|
112
|
-
| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.find(…)`. | ✅ | 🔧 | 💡 |
|
|
112
|
+
| [prefer-array-index-of](docs/rules/prefer-array-index-of.md) | Prefer `Array#{indexOf,lastIndexOf}()` over `Array#{findIndex,findLastIndex}()` when looking for the index of an item. | ✅ | 🔧 | 💡 |
|
|
113
|
+
| [prefer-array-some](docs/rules/prefer-array-some.md) | Prefer `.some(…)` over `.filter(…).length` check and `.{find,findLast}(…)`. | ✅ | 🔧 | 💡 |
|
|
113
114
|
| [prefer-at](docs/rules/prefer-at.md) | Prefer `.at()` method for index access and `String#charAt()`. | | 🔧 | 💡 |
|
|
114
115
|
| [prefer-code-point](docs/rules/prefer-code-point.md) | Prefer `String#codePointAt(…)` over `String#charCodeAt(…)` and `String.fromCodePoint(…)` over `String.fromCharCode(…)`. | ✅ | | 💡 |
|
|
115
116
|
| [prefer-date-now](docs/rules/prefer-date-now.md) | Prefer `Date.now()` to get the number of milliseconds since the Unix Epoch. | ✅ | 🔧 | |
|
|
@@ -129,7 +130,7 @@ Each rule has emojis denoting:
|
|
|
129
130
|
| [prefer-modern-math-apis](docs/rules/prefer-modern-math-apis.md) | Prefer modern `Math` APIs over legacy patterns. | ✅ | 🔧 | |
|
|
130
131
|
| [prefer-module](docs/rules/prefer-module.md) | Prefer JavaScript modules (ESM) over CommonJS. | ✅ | 🔧 | 💡 |
|
|
131
132
|
| [prefer-native-coercion-functions](docs/rules/prefer-native-coercion-functions.md) | Prefer using `String`, `Number`, `BigInt`, `Boolean`, and `Symbol` directly. | ✅ | 🔧 | |
|
|
132
|
-
| [prefer-negative-index](docs/rules/prefer-negative-index.md) | Prefer negative index over `.length - index` for `{String,Array,TypedArray}#slice()
|
|
133
|
+
| [prefer-negative-index](docs/rules/prefer-negative-index.md) | Prefer negative index over `.length - index` for `{String,Array,TypedArray}#{slice,at}()` and `Array#splice()`. | ✅ | 🔧 | |
|
|
133
134
|
| [prefer-node-protocol](docs/rules/prefer-node-protocol.md) | Prefer using the `node:` protocol when importing Node.js builtin modules. | ✅ | 🔧 | |
|
|
134
135
|
| [prefer-number-properties](docs/rules/prefer-number-properties.md) | Prefer `Number` static properties over global ones. | ✅ | 🔧 | 💡 |
|
|
135
136
|
| [prefer-object-from-entries](docs/rules/prefer-object-from-entries.md) | Prefer using `Object.fromEntries(…)` to transform a list of key-value pairs into an object. | ✅ | 🔧 | |
|
|
@@ -154,6 +155,7 @@ Each rule has emojis denoting:
|
|
|
154
155
|
| [require-number-to-fixed-digits-argument](docs/rules/require-number-to-fixed-digits-argument.md) | Enforce using the digits argument with `Number#toFixed()`. | ✅ | 🔧 | |
|
|
155
156
|
| [require-post-message-target-origin](docs/rules/require-post-message-target-origin.md) | Enforce using the `targetOrigin` argument with `window.postMessage()`. | | | 💡 |
|
|
156
157
|
| [string-content](docs/rules/string-content.md) | Enforce better string content. | | 🔧 | 💡 |
|
|
158
|
+
| [switch-case-braces](docs/rules/switch-case-braces.md) | Enforce consistent brace style for `case` clauses. | ✅ | 🔧 | |
|
|
157
159
|
| [template-indent](docs/rules/template-indent.md) | Fix whitespace-insensitive template indentation. | ✅ | 🔧 | |
|
|
158
160
|
| [text-encoding-identifier-case](docs/rules/text-encoding-identifier-case.md) | Enforce consistent case for text encoding identifiers. | ✅ | 🔧 | 💡 |
|
|
159
161
|
| [throw-new-error](docs/rules/throw-new-error.md) | Require `new` when throwing an error. | ✅ | 🔧 | |
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
const {isStringLiteral} = require('./literal.js');
|
|
4
4
|
|
|
5
5
|
const isStaticRequire = node => Boolean(
|
|
6
|
-
node
|
|
7
|
-
&& node.type === 'CallExpression'
|
|
8
|
-
&& node.callee
|
|
6
|
+
node?.type === 'CallExpression'
|
|
9
7
|
&& node.callee.type === 'Identifier'
|
|
10
8
|
&& node.callee.name === 'require'
|
|
11
9
|
&& !node.optional
|
package/rules/better-regex.js
CHANGED
|
@@ -51,15 +51,32 @@ const create = context => {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
const problem = {
|
|
55
55
|
node,
|
|
56
56
|
messageId: MESSAGE_ID,
|
|
57
57
|
data: {
|
|
58
58
|
original,
|
|
59
59
|
optimized,
|
|
60
60
|
},
|
|
61
|
-
fix: fixer => fixer.replaceText(node, optimized),
|
|
62
61
|
};
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
node.parent.type === 'MemberExpression'
|
|
65
|
+
&& node.parent.object === node
|
|
66
|
+
&& !node.parent.optional
|
|
67
|
+
&& !node.parent.computed
|
|
68
|
+
&& node.parent.property.type === 'Identifier'
|
|
69
|
+
&& (
|
|
70
|
+
node.parent.property.name === 'toString'
|
|
71
|
+
|| node.parent.property.name === 'source'
|
|
72
|
+
)
|
|
73
|
+
) {
|
|
74
|
+
return problem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Object.assign(problem, {
|
|
78
|
+
fix: fixer => fixer.replaceText(node, optimized),
|
|
79
|
+
});
|
|
63
80
|
},
|
|
64
81
|
[newRegExp](node) {
|
|
65
82
|
const [patternNode, flagsNode] = node.arguments;
|
|
@@ -21,7 +21,7 @@ function checkReferences(scope, parent, scopeManager) {
|
|
|
21
21
|
const [definition] = resolved.defs;
|
|
22
22
|
|
|
23
23
|
// Skip recursive function name
|
|
24
|
-
if (definition
|
|
24
|
+
if (definition?.type === 'FunctionName' && resolved.name === definition.name.name) {
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -92,23 +92,20 @@ const reactHooks = [
|
|
|
92
92
|
].flatMap(hookName => [hookName, `React.${hookName}`]);
|
|
93
93
|
|
|
94
94
|
const isReactHook = scope =>
|
|
95
|
-
scope.block
|
|
96
|
-
&& scope.block.parent
|
|
97
|
-
&& scope.block.parent.callee
|
|
95
|
+
scope.block?.parent?.callee
|
|
98
96
|
&& isNodeMatches(scope.block.parent.callee, reactHooks);
|
|
99
97
|
|
|
100
98
|
const isArrowFunctionWithThis = scope =>
|
|
101
99
|
scope.type === 'function'
|
|
102
|
-
&& scope.block
|
|
103
|
-
&& scope.block.type === 'ArrowFunctionExpression'
|
|
100
|
+
&& scope.block?.type === 'ArrowFunctionExpression'
|
|
104
101
|
&& (scope.thisFound || scope.childScopes.some(scope => isArrowFunctionWithThis(scope)));
|
|
105
102
|
|
|
106
103
|
const iifeFunctionTypes = new Set([
|
|
107
104
|
'FunctionExpression',
|
|
108
105
|
'ArrowFunctionExpression',
|
|
109
106
|
]);
|
|
110
|
-
const isIife = node =>
|
|
111
|
-
|
|
107
|
+
const isIife = node =>
|
|
108
|
+
iifeFunctionTypes.has(node.type)
|
|
112
109
|
&& node.parent.type === 'CallExpression'
|
|
113
110
|
&& node.parent.callee === node;
|
|
114
111
|
|
|
@@ -137,15 +137,15 @@ function * customErrorDefinition(context, node) {
|
|
|
137
137
|
if (!nameExpression) {
|
|
138
138
|
const nameProperty = body.find(node => isPropertyDefinition(node, 'name'));
|
|
139
139
|
|
|
140
|
-
if (!nameProperty
|
|
140
|
+
if (!nameProperty?.value || nameProperty.value.value !== name) {
|
|
141
141
|
yield {
|
|
142
|
-
node: nameProperty
|
|
142
|
+
node: nameProperty?.value ?? constructorBodyNode,
|
|
143
143
|
message: `The \`name\` property should be set to \`${name}\`.`,
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
} else if (nameExpression.expression.right.value !== name) {
|
|
147
147
|
yield {
|
|
148
|
-
node: nameExpression
|
|
148
|
+
node: nameExpression?.expression.right ?? constructorBodyNode,
|
|
149
149
|
message: `The \`name\` property should be set to \`${name}\`.`,
|
|
150
150
|
};
|
|
151
151
|
}
|
package/rules/filename-case.js
CHANGED
|
@@ -107,7 +107,7 @@ function splitFilename(filename) {
|
|
|
107
107
|
for (const char of tailing) {
|
|
108
108
|
const isIgnored = isIgnoredChar(char);
|
|
109
109
|
|
|
110
|
-
if (lastWord
|
|
110
|
+
if (lastWord?.ignored === isIgnored) {
|
|
111
111
|
lastWord.word += char;
|
|
112
112
|
} else {
|
|
113
113
|
lastWord = {
|
|
@@ -147,7 +147,7 @@ const create = context => {
|
|
|
147
147
|
const filenameWithExtension = context.getPhysicalFilename();
|
|
148
148
|
|
|
149
149
|
if (filenameWithExtension === '<input>' || filenameWithExtension === '<text>') {
|
|
150
|
-
return
|
|
150
|
+
return;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
return {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const {isSemicolonToken} = require('eslint-utils');
|
|
3
|
+
|
|
4
|
+
function * addParenthesizesToReturnOrThrowExpression(fixer, node, sourceCode) {
|
|
5
|
+
if (node.type !== 'ReturnStatement' && node.type !== 'ThrowStatement') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const returnOrThrowToken = sourceCode.getFirstToken(node);
|
|
10
|
+
yield fixer.insertTextAfter(returnOrThrowToken, ' (');
|
|
11
|
+
|
|
12
|
+
const lastToken = sourceCode.getLastToken(node);
|
|
13
|
+
if (!isSemicolonToken(lastToken)) {
|
|
14
|
+
yield fixer.insertTextAfter(node, ')');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
yield fixer.insertTextBefore(lastToken, ')');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = addParenthesizesToReturnOrThrowExpression;
|
package/rules/fix/index.js
CHANGED
|
@@ -19,4 +19,5 @@ module.exports = {
|
|
|
19
19
|
removeSpacesAfter: require('./remove-spaces-after.js'),
|
|
20
20
|
fixSpaceAroundKeyword: require('./fix-space-around-keywords.js'),
|
|
21
21
|
replaceStringLiteral: require('./replace-string-literal.js'),
|
|
22
|
+
addParenthesizesToReturnOrThrowExpression: require('./add-parenthesizes-to-return-or-throw-expression.js'),
|
|
22
23
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function removeSpacesAfter(
|
|
4
|
-
let index =
|
|
5
|
-
if (typeof
|
|
6
|
-
index =
|
|
3
|
+
function removeSpacesAfter(indexOrNodeOrToken, sourceCode, fixer) {
|
|
4
|
+
let index = indexOrNodeOrToken;
|
|
5
|
+
if (typeof indexOrNodeOrToken === 'object' && Array.isArray(indexOrNodeOrToken.range)) {
|
|
6
|
+
index = indexOrNodeOrToken.range[1];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const textAfter = sourceCode.text.slice(index);
|
|
@@ -1,41 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const isNewExpressionWithParentheses = require('../utils/is-new-expression-with-parentheses.js');
|
|
3
3
|
const {isParenthesized} = require('../utils/parentheses.js');
|
|
4
|
+
const isOnSameLine = require('../utils/is-on-same-line.js');
|
|
5
|
+
const addParenthesizesToReturnOrThrowExpression = require('./add-parenthesizes-to-return-or-throw-expression.js');
|
|
6
|
+
const removeSpaceAfter = require('./remove-spaces-after.js');
|
|
4
7
|
|
|
5
|
-
function *
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|| parent.argument !== newExpression
|
|
10
|
-
|| isParenthesized(newExpression, sourceCode)
|
|
11
|
-
) {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const returnStatement = parent;
|
|
16
|
-
const returnToken = sourceCode.getFirstToken(returnStatement);
|
|
17
|
-
const classNode = newExpression.callee;
|
|
18
|
-
|
|
19
|
-
// Ideally, we should use first parenthesis of the `callee`, and should check spaces after the `new` token
|
|
20
|
-
// But adding extra parentheses is harmless, no need to be too complicated
|
|
21
|
-
if (returnToken.loc.start.line === classNode.loc.start.line) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
8
|
+
function * switchNewExpressionToCallExpression(newExpression, sourceCode, fixer) {
|
|
9
|
+
const newToken = sourceCode.getFirstToken(newExpression);
|
|
10
|
+
yield fixer.remove(newToken);
|
|
11
|
+
yield removeSpaceAfter(newToken, sourceCode, fixer);
|
|
24
12
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function * switchNewExpressionToCallExpression(node, sourceCode, fixer) {
|
|
30
|
-
const [start] = node.range;
|
|
31
|
-
let end = start + 3; // `3` = length of `new`
|
|
32
|
-
const textAfter = sourceCode.text.slice(end);
|
|
33
|
-
const [leadingSpaces] = textAfter.match(/^\s*/);
|
|
34
|
-
end += leadingSpaces.length;
|
|
35
|
-
yield fixer.removeRange([start, end]);
|
|
36
|
-
|
|
37
|
-
if (!isNewExpressionWithParentheses(node, sourceCode)) {
|
|
38
|
-
yield fixer.insertTextAfter(node, '()');
|
|
13
|
+
if (!isNewExpressionWithParentheses(newExpression, sourceCode)) {
|
|
14
|
+
yield fixer.insertTextAfter(newExpression, '()');
|
|
39
15
|
}
|
|
40
16
|
|
|
41
17
|
/*
|
|
@@ -48,7 +24,11 @@ function * switchNewExpressionToCallExpression(node, sourceCode, fixer) {
|
|
|
48
24
|
}
|
|
49
25
|
```
|
|
50
26
|
*/
|
|
51
|
-
|
|
27
|
+
if (!isOnSameLine(newToken, newExpression.callee) && !isParenthesized(newExpression, sourceCode)) {
|
|
28
|
+
// Ideally, we should use first parenthesis of the `callee`, and should check spaces after the `new` token
|
|
29
|
+
// But adding extra parentheses is harmless, no need to be too complicated
|
|
30
|
+
yield * addParenthesizesToReturnOrThrowExpression(fixer, newExpression.parent, sourceCode);
|
|
31
|
+
}
|
|
52
32
|
}
|
|
53
33
|
|
|
54
34
|
module.exports = switchNewExpressionToCallExpression;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const {
|
|
2
|
+
const {GlobalReferenceTracker} = require('./utils/global-reference-tracker.js');
|
|
3
3
|
const builtins = require('./utils/builtins.js');
|
|
4
4
|
const {
|
|
5
5
|
switchCallExpressionToNewExpression,
|
|
@@ -11,64 +11,65 @@ const messages = {
|
|
|
11
11
|
disallow: 'Use `{{name}}()` instead of `new {{name}}()`.',
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
parent.type === 'BinaryExpression'
|
|
24
|
-
&& (parent.operator === '===' || parent.operator === '!==')
|
|
25
|
-
&& (parent.left === node || parent.right === node)
|
|
26
|
-
) {
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
14
|
+
function enforceNewExpression({node, path: [name]}, sourceCode) {
|
|
15
|
+
if (name === 'Object') {
|
|
16
|
+
const {parent} = node;
|
|
17
|
+
if (
|
|
18
|
+
parent.type === 'BinaryExpression'
|
|
19
|
+
&& (parent.operator === '===' || parent.operator === '!==')
|
|
20
|
+
&& (parent.left === node || parent.right === node)
|
|
21
|
+
) {
|
|
22
|
+
return;
|
|
29
23
|
}
|
|
30
|
-
|
|
31
|
-
yield {
|
|
32
|
-
node,
|
|
33
|
-
messageId: 'enforce',
|
|
34
|
-
data: {name},
|
|
35
|
-
fix: fixer => switchCallExpressionToNewExpression(node, sourceCode, fixer),
|
|
36
|
-
};
|
|
37
24
|
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
node,
|
|
28
|
+
messageId: 'enforce',
|
|
29
|
+
data: {name},
|
|
30
|
+
fix: fixer => switchCallExpressionToNewExpression(node, sourceCode, fixer),
|
|
31
|
+
};
|
|
38
32
|
}
|
|
39
33
|
|
|
40
|
-
function
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
function enforceCallExpression({node, path: [name]}, sourceCode) {
|
|
35
|
+
const problem = {
|
|
36
|
+
node,
|
|
37
|
+
messageId: 'disallow',
|
|
38
|
+
data: {name},
|
|
39
|
+
};
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
node,
|
|
48
|
-
messageId: 'disallow',
|
|
49
|
-
data: {name},
|
|
41
|
+
if (name !== 'String' && name !== 'Boolean' && name !== 'Number') {
|
|
42
|
+
problem.fix = function * (fixer) {
|
|
43
|
+
yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
|
|
50
44
|
};
|
|
51
|
-
|
|
52
|
-
if (name !== 'String' && name !== 'Boolean' && name !== 'Number') {
|
|
53
|
-
problem.fix = function * (fixer) {
|
|
54
|
-
yield * switchNewExpressionToCallExpression(node, sourceCode, fixer);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
yield problem;
|
|
59
45
|
}
|
|
46
|
+
|
|
47
|
+
return problem;
|
|
60
48
|
}
|
|
61
49
|
|
|
62
50
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
63
|
-
const create = context =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
const create = context => {
|
|
52
|
+
const sourceCode = context.getSourceCode();
|
|
53
|
+
const newExpressionTracker = new GlobalReferenceTracker({
|
|
54
|
+
objects: builtins.disallowNew,
|
|
55
|
+
type: GlobalReferenceTracker.CONSTRUCT,
|
|
56
|
+
handle: reference => enforceCallExpression(reference, sourceCode),
|
|
57
|
+
});
|
|
58
|
+
const callExpressionTracker = new GlobalReferenceTracker({
|
|
59
|
+
objects: builtins.enforceNew,
|
|
60
|
+
type: GlobalReferenceTracker.CALL,
|
|
61
|
+
handle: reference => enforceNewExpression(reference, sourceCode),
|
|
62
|
+
});
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
return {
|
|
65
|
+
* 'Program:exit'() {
|
|
66
|
+
const scope = context.getScope();
|
|
67
|
+
|
|
68
|
+
yield * newExpressionTracker.track(scope);
|
|
69
|
+
yield * callExpressionTracker.track(scope);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
72
73
|
|
|
73
74
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
74
75
|
module.exports = {
|
|
@@ -39,6 +39,14 @@ const iteratorMethods = [
|
|
|
39
39
|
],
|
|
40
40
|
},
|
|
41
41
|
],
|
|
42
|
+
[
|
|
43
|
+
'findLast',
|
|
44
|
+
{
|
|
45
|
+
ignore: [
|
|
46
|
+
'Boolean',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
42
50
|
[
|
|
43
51
|
'findIndex',
|
|
44
52
|
{
|
|
@@ -47,6 +55,14 @@ const iteratorMethods = [
|
|
|
47
55
|
],
|
|
48
56
|
},
|
|
49
57
|
],
|
|
58
|
+
[
|
|
59
|
+
'findLastIndex',
|
|
60
|
+
{
|
|
61
|
+
ignore: [
|
|
62
|
+
'Boolean',
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
50
66
|
[
|
|
51
67
|
'flatMap',
|
|
52
68
|
],
|
|
@@ -59,8 +59,9 @@ function shouldSwitchReturnStatementToBlockStatement(returnStatement) {
|
|
|
59
59
|
const {parent} = returnStatement;
|
|
60
60
|
|
|
61
61
|
switch (parent.type) {
|
|
62
|
-
case 'IfStatement':
|
|
62
|
+
case 'IfStatement': {
|
|
63
63
|
return parent.consequent === returnStatement || parent.alternate === returnStatement;
|
|
64
|
+
}
|
|
64
65
|
|
|
65
66
|
// These parent's body need switch to `BlockStatement` too, but since they are "continueAble", won't fix
|
|
66
67
|
// case 'ForStatement':
|
|
@@ -68,11 +69,13 @@ function shouldSwitchReturnStatementToBlockStatement(returnStatement) {
|
|
|
68
69
|
// case 'ForOfStatement':
|
|
69
70
|
// case 'WhileStatement':
|
|
70
71
|
// case 'DoWhileStatement':
|
|
71
|
-
case 'WithStatement':
|
|
72
|
+
case 'WithStatement': {
|
|
72
73
|
return parent.body === returnStatement;
|
|
74
|
+
}
|
|
73
75
|
|
|
74
|
-
default:
|
|
76
|
+
default: {
|
|
75
77
|
return false;
|
|
78
|
+
}
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -315,20 +318,33 @@ function isAssignmentLeftHandSide(node) {
|
|
|
315
318
|
switch (parent.type) {
|
|
316
319
|
case 'AssignmentExpression':
|
|
317
320
|
case 'ForInStatement':
|
|
318
|
-
case 'ForOfStatement':
|
|
321
|
+
case 'ForOfStatement': {
|
|
319
322
|
return parent.left === node;
|
|
320
|
-
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
case 'UpdateExpression': {
|
|
321
326
|
return parent.argument === node;
|
|
322
|
-
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
case 'Property': {
|
|
323
330
|
return parent.value === node && isAssignmentLeftHandSide(parent);
|
|
324
|
-
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
case 'AssignmentPattern': {
|
|
325
334
|
return parent.left === node && isAssignmentLeftHandSide(parent);
|
|
326
|
-
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
case 'ArrayPattern': {
|
|
327
338
|
return parent.elements.includes(node) && isAssignmentLeftHandSide(parent);
|
|
328
|
-
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
case 'ObjectPattern': {
|
|
329
342
|
return parent.properties.includes(node) && isAssignmentLeftHandSide(parent);
|
|
330
|
-
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
default: {
|
|
331
346
|
return false;
|
|
347
|
+
}
|
|
332
348
|
}
|
|
333
349
|
}
|
|
334
350
|
|
|
@@ -30,6 +30,7 @@ const create = context => {
|
|
|
30
30
|
&& memberExpression.parent.type === 'VariableDeclarator'
|
|
31
31
|
&& memberExpression.parent.init === memberExpression
|
|
32
32
|
&& memberExpression.parent.id.type === 'Identifier'
|
|
33
|
+
&& !memberExpression.parent.id.typeAnnotation
|
|
33
34
|
) {
|
|
34
35
|
problem.fix = function * (fixer) {
|
|
35
36
|
const variable = memberExpression.parent.id;
|
|
@@ -52,6 +53,7 @@ const create = context => {
|
|
|
52
53
|
&& memberExpression.parent.init === memberExpression
|
|
53
54
|
&& memberExpression.parent.id.type === 'Identifier'
|
|
54
55
|
&& memberExpression.parent.id.name === property.name
|
|
56
|
+
&& !memberExpression.parent.id.typeAnnotation
|
|
55
57
|
) {
|
|
56
58
|
problem.fix = function * (fixer) {
|
|
57
59
|
const variable = memberExpression.parent.id;
|