eslint-plugin-unicorn 70.0.0 → 71.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/package.json +26 -25
- package/readme.md +5 -1
- package/rules/ast/call-or-new-expression.js +2 -8
- package/rules/ast/is-member-expression.js +2 -8
- package/rules/comment-content.js +4 -4
- package/rules/consistent-boolean-name.js +94 -10
- package/rules/consistent-compound-words.js +1 -2
- package/rules/consistent-json-file-read.js +2 -5
- package/rules/dom-node-dataset.js +3 -7
- package/rules/escape-case.js +4 -4
- package/rules/expiring-todo-comments.js +2 -2
- package/rules/explicit-length-check.js +3 -3
- package/rules/filename-case.js +4 -4
- package/rules/fix/remove-argument.js +34 -8
- package/rules/fix/remove-statement.js +1 -3
- package/rules/id-match.js +2 -2
- package/rules/isolated-functions.js +47 -7
- package/rules/name-replacements.js +1 -2
- package/rules/no-anonymous-default-export.js +1 -3
- package/rules/no-array-callback-reference.js +2 -10
- package/rules/no-chained-comparison.js +1 -1
- package/rules/no-computed-property-existence-check.js +1 -4
- package/rules/no-declarations-before-early-exit.js +6 -5
- package/rules/no-error-property-assignment.js +3 -3
- package/rules/no-for-loop.js +4 -4
- package/rules/no-keyword-prefix.js +2 -2
- package/rules/no-mismatched-map-key.js +13 -20
- package/rules/no-named-default.js +2 -5
- package/rules/no-negated-array-predicate.js +4 -11
- package/rules/no-negated-comparison.js +7 -10
- package/rules/no-negation-in-equality-check.js +2 -5
- package/rules/no-process-exit.js +6 -6
- package/rules/no-static-only-class.js +2 -8
- package/rules/no-thenable.js +9 -15
- package/rules/no-undeclared-class-members.js +1 -5
- package/rules/no-unnecessary-array-flat-map.js +6 -5
- package/rules/no-unnecessary-global-this.js +5 -9
- package/rules/no-unused-properties.js +0 -1
- package/rules/no-useless-coercion.js +1 -2
- package/rules/no-useless-undefined.js +2 -2
- package/rules/prefer-array-find.js +2 -5
- package/rules/prefer-bigint-literals.js +1 -0
- package/rules/prefer-boolean-return.js +20 -3
- package/rules/prefer-default-parameters.js +2 -2
- package/rules/prefer-else-if.js +5 -9
- package/rules/prefer-has-check.js +6 -3
- package/rules/prefer-https.js +3 -3
- package/rules/prefer-identifier-import-export-specifiers.js +1 -3
- package/rules/prefer-minimal-ternary.js +4 -4
- package/rules/prefer-object-define-properties.js +1 -3
- package/rules/prefer-observer-apis.js +2 -5
- package/rules/prefer-private-class-fields.js +1 -3
- package/rules/prefer-simple-sort-comparator.js +6 -6
- package/rules/prefer-single-object-destructuring.js +2 -5
- package/rules/prefer-single-replace.js +2 -2
- package/rules/prefer-spread.js +22 -32
- package/rules/prefer-string-pad-start-end.js +1 -6
- package/rules/prefer-string-repeat.js +16 -9
- package/rules/prefer-string-replace-all.js +1 -1
- package/rules/prefer-ternary.js +5 -5
- package/rules/prefer-toggle-attribute.js +3 -3
- package/rules/prefer-top-level-await.js +6 -1
- package/rules/prefer-uint8array-base64.js +3 -1
- package/rules/require-proxy-trap-boolean-return.js +2 -2
- package/rules/rule/to-eslint-create.js +1 -2
- package/rules/rule/to-eslint-listener.js +1 -3
- package/rules/rule/to-eslint-problem.js +1 -3
- package/rules/rule/to-eslint-rule-fixer.js +1 -3
- package/rules/rule/to-eslint-rule.js +1 -3
- package/rules/rule/to-eslint-rules.js +1 -3
- package/rules/rule/unicorn-context.js +1 -3
- package/rules/rule/unicorn-listeners.js +1 -3
- package/rules/shared/no-array-mutate-rule.js +1 -3
- package/rules/shared/regexp-escape.js +4 -4
- package/rules/utils/can-tokens-be-adjacent.js +95 -0
- package/rules/utils/escape-string.js +2 -8
- package/rules/utils/get-available-variable-name.js +2 -13
- package/rules/utils/get-precedence.js +112 -0
- package/rules/utils/get-token-store.js +1 -3
- package/rules/utils/global-reference-tracker.js +3 -6
- package/rules/utils/index.js +4 -0
- package/rules/utils/is-boolean.js +90 -32
- package/rules/utils/is-identifier-name.js +5 -0
- package/rules/utils/is-promise-type.js +1 -3
- package/rules/utils/is-react-hook-name.js +5 -0
- package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
- package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
- package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
- package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
- package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
- package/rules/utils/to-location.js +1 -3
|
@@ -224,15 +224,15 @@ const create = context => {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
const setCall = consequentCall.method === 'setAttribute' ? consequentCall : alternateCall;
|
|
227
|
-
const
|
|
227
|
+
const isSetWhenTrue = consequentCall === setCall;
|
|
228
228
|
const hasAttributeCondition = getHasAttributeCondition(node.test, setCall, context);
|
|
229
229
|
|
|
230
230
|
if (hasAttributeCondition?.isKnownNonDom) {
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
const shouldToggleWithoutForce = Boolean(hasAttributeCondition) &&
|
|
235
|
-
const conditionText = shouldToggleWithoutForce ? '' : getConditionText(node.test, context, !
|
|
234
|
+
const shouldToggleWithoutForce = Boolean(hasAttributeCondition) && isSetWhenTrue === hasAttributeCondition.isNegative;
|
|
235
|
+
const conditionText = shouldToggleWithoutForce ? '' : getConditionText(node.test, context, !isSetWhenTrue);
|
|
236
236
|
const hasSafeAttributeValue = setCall.hasEmptyAttributeValue || getStaticStringValue(setCall.attributeValue) !== undefined;
|
|
237
237
|
const shouldReportOnly = (Boolean(conditionText) && setCall.isOptional) || !hasSafeAttributeValue;
|
|
238
238
|
|
|
@@ -205,7 +205,12 @@ const isInPromiseMethods = node => {
|
|
|
205
205
|
|
|
206
206
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
207
207
|
function create(context) {
|
|
208
|
-
|
|
208
|
+
// Use the real file path so processors or code blocks cannot hide file-level opt-outs.
|
|
209
|
+
const filename = context.physicalFilename.toLowerCase();
|
|
210
|
+
if (
|
|
211
|
+
filename.endsWith('.cjs')
|
|
212
|
+
|| filename.endsWith('.svelte')
|
|
213
|
+
) {
|
|
209
214
|
return;
|
|
210
215
|
}
|
|
211
216
|
|
|
@@ -152,7 +152,9 @@ const config = {
|
|
|
152
152
|
type: 'suggestion',
|
|
153
153
|
docs: {
|
|
154
154
|
description: 'Prefer `Uint8Array#toBase64()` and `Uint8Array.fromBase64()` over `atob()`, `btoa()`, and `Buffer` base64 conversions.',
|
|
155
|
-
|
|
155
|
+
// eslint-disable-next-line no-warning-comments
|
|
156
|
+
// TODO: Enable in the `recommended` and `unopinionated` configs when targeting Node.js 26.
|
|
157
|
+
recommended: false,
|
|
156
158
|
},
|
|
157
159
|
hasSuggestions: true,
|
|
158
160
|
messages,
|
|
@@ -277,8 +277,8 @@ const create = context => {
|
|
|
277
277
|
return;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
const
|
|
281
|
-
functionBodyAlwaysExits.set(body.parent,
|
|
280
|
+
const isAllUnreachable = [...currentSegments()].every(segment => !segment.reachable);
|
|
281
|
+
functionBodyAlwaysExits.set(body.parent, isAllUnreachable);
|
|
282
282
|
});
|
|
283
283
|
|
|
284
284
|
function * checkCallOrNewExpression(node) {
|
|
@@ -26,7 +26,7 @@ Convert Unicorn style of `create` to ESLint style
|
|
|
26
26
|
@param {UnicornCreate} unicornCreate
|
|
27
27
|
@returns {EslintCreate}
|
|
28
28
|
*/
|
|
29
|
-
function toEslintCreate(unicornCreate) {
|
|
29
|
+
export default function toEslintCreate(unicornCreate) {
|
|
30
30
|
if (wrappedFunctions.has(unicornCreate)) {
|
|
31
31
|
return unicornCreate;
|
|
32
32
|
}
|
|
@@ -45,5 +45,4 @@ function toEslintCreate(unicornCreate) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export default toEslintCreate;
|
|
49
48
|
export {markFunctionWrapped};
|
|
@@ -19,7 +19,7 @@ import toEslintProblem from './to-eslint-problem.js';
|
|
|
19
19
|
@param {UnicornRuleListen} listener
|
|
20
20
|
@returns {Listener}
|
|
21
21
|
*/
|
|
22
|
-
function toEslintListener(context, listener) {
|
|
22
|
+
export default function toEslintListener(context, listener) {
|
|
23
23
|
// Listener arguments can be `codePath, node` or `node`
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -38,5 +38,3 @@ function toEslintListener(context, listener) {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
export default toEslintListener;
|
|
@@ -14,7 +14,7 @@ import toEslintFixer from './to-eslint-rule-fixer.js';
|
|
|
14
14
|
@param {UnicornProblem} unicornProblem
|
|
15
15
|
@returns {EslintProblem}
|
|
16
16
|
*/
|
|
17
|
-
function toEslintProblem(unicornProblem) {
|
|
17
|
+
export default function toEslintProblem(unicornProblem) {
|
|
18
18
|
const eslintProblem = {...unicornProblem};
|
|
19
19
|
|
|
20
20
|
if (unicornProblem.fix) {
|
|
@@ -34,5 +34,3 @@ function toEslintProblem(unicornProblem) {
|
|
|
34
34
|
|
|
35
35
|
return eslintProblem;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
export default toEslintProblem;
|
|
@@ -26,7 +26,7 @@ Convert Unicorn style fix function to ESLint style fix function
|
|
|
26
26
|
@param {UnicornRuleFixer} fix
|
|
27
27
|
@returns {ESLint.Rule.RuleFixer}
|
|
28
28
|
*/
|
|
29
|
-
function toEslintRuleFixer(fix) {
|
|
29
|
+
export default function toEslintRuleFixer(fix) {
|
|
30
30
|
/** @param {UnicornReportFixer} fixer */
|
|
31
31
|
return fixer => {
|
|
32
32
|
const unicornReport = fix(fixer, fixOptions);
|
|
@@ -45,5 +45,3 @@ function toEslintRuleFixer(fix) {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
export default toEslintRuleFixer;
|
|
@@ -19,7 +19,7 @@ Convert Unicorn rule to ESLint rule
|
|
|
19
19
|
@param {UnicornRule} unicornRule
|
|
20
20
|
@returns {ESLint.Rule.RuleModule}
|
|
21
21
|
*/
|
|
22
|
-
function toEslintRule(ruleId, unicornRule) {
|
|
22
|
+
export default function toEslintRule(ruleId, unicornRule) {
|
|
23
23
|
return {
|
|
24
24
|
meta: {
|
|
25
25
|
// If there is are, options add `[]` so ESLint can validate that no data is passed to the rule.
|
|
@@ -34,5 +34,3 @@ function toEslintRule(ruleId, unicornRule) {
|
|
|
34
34
|
create: toEslintCreate(unicornRule.create),
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
export default toEslintRule;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import toEslintRule from './to-eslint-rule.js';
|
|
2
2
|
|
|
3
|
-
function toEslintRules(rules) {
|
|
3
|
+
export default function toEslintRules(rules) {
|
|
4
4
|
return Object.fromEntries(Object.entries(rules).map(([ruleId, rule]) => [
|
|
5
5
|
ruleId,
|
|
6
6
|
toEslintRule(ruleId, rule),
|
|
7
7
|
]));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export default toEslintRules;
|
|
11
|
-
|
|
@@ -18,7 +18,7 @@ Create a better `Context` object with `on` and `onExit` method to add listeners
|
|
|
18
18
|
@param {UnicornListeners} listeners
|
|
19
19
|
@returns {UnicornContext}
|
|
20
20
|
*/
|
|
21
|
-
function createUnicornContext(eslintContext, listeners) {
|
|
21
|
+
export default function createUnicornContext(eslintContext, listeners) {
|
|
22
22
|
/** @type {UnicornContext} */
|
|
23
23
|
const context = new Proxy(eslintContext, {
|
|
24
24
|
get(target, property, receiver) {
|
|
@@ -32,5 +32,3 @@ function createUnicornContext(eslintContext, listeners) {
|
|
|
32
32
|
|
|
33
33
|
return context;
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
export default createUnicornContext;
|
|
@@ -4,7 +4,7 @@ import toEslintListener from './to-eslint-listener.js';
|
|
|
4
4
|
@import {EslintListers, ListenerType, Listener} from './to-eslint-create.js'
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
class UnicornListeners {
|
|
7
|
+
export default class UnicornListeners {
|
|
8
8
|
#context;
|
|
9
9
|
#listeners = new Map();
|
|
10
10
|
|
|
@@ -61,5 +61,3 @@ class UnicornListeners {
|
|
|
61
61
|
return eslintListeners;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
export default UnicornListeners;
|
|
@@ -50,7 +50,7 @@ const schema = [
|
|
|
50
50
|
},
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
-
function noArrayMutateRule(methodName) {
|
|
53
|
+
export default function noArrayMutateRule(methodName) {
|
|
54
54
|
const {
|
|
55
55
|
replacement,
|
|
56
56
|
predicate,
|
|
@@ -159,5 +159,3 @@ function noArrayMutateRule(methodName) {
|
|
|
159
159
|
|
|
160
160
|
return config;
|
|
161
161
|
}
|
|
162
|
-
|
|
163
|
-
export default noArrayMutateRule;
|
|
@@ -19,12 +19,12 @@ const getCharacterClassCharacters = pattern => {
|
|
|
19
19
|
|
|
20
20
|
for (let index = 0; index < characterClass.length; index++) {
|
|
21
21
|
let character = characterClass[index];
|
|
22
|
-
let
|
|
22
|
+
let isEscaped = false;
|
|
23
23
|
|
|
24
24
|
if (character === '\\') {
|
|
25
25
|
index++;
|
|
26
26
|
character = characterClass[index];
|
|
27
|
-
|
|
27
|
+
isEscaped = true;
|
|
28
28
|
|
|
29
29
|
if (!character) {
|
|
30
30
|
return;
|
|
@@ -33,7 +33,7 @@ const getCharacterClassCharacters = pattern => {
|
|
|
33
33
|
|
|
34
34
|
if (
|
|
35
35
|
character === '-'
|
|
36
|
-
&& !
|
|
36
|
+
&& !isEscaped
|
|
37
37
|
&& index !== 0
|
|
38
38
|
&& index !== characterClass.length - 1
|
|
39
39
|
) {
|
|
@@ -42,7 +42,7 @@ const getCharacterClassCharacters = pattern => {
|
|
|
42
42
|
|
|
43
43
|
if (
|
|
44
44
|
character === ']'
|
|
45
|
-
&& !
|
|
45
|
+
&& !isEscaped
|
|
46
46
|
&& index !== 0
|
|
47
47
|
) {
|
|
48
48
|
return;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@import * as ESLint from 'eslint';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const WORD_CHARACTER = /[\w$]/u;
|
|
6
|
+
const DECIMAL_INTEGER_LITERAL = /^\d(?:_?\d)*$/u;
|
|
7
|
+
const TRAILING_DECIMAL_INTEGER_LITERAL = /(?:^|[^\w$])\d(?:_?\d)*$/u;
|
|
8
|
+
const TRAILING_DIGIT_DOT = /\d\.$/u;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
@param {ESLint.AST.Token | string} value
|
|
12
|
+
@returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function getText(value) {
|
|
15
|
+
return typeof value === 'string' ? value : value.value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
@param {ESLint.AST.Token | string} value
|
|
20
|
+
@returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function isLineCommentOrShebang(value) {
|
|
23
|
+
return typeof value === 'object' && ['Line', 'Shebang', 'Hashbang'].includes(value.type);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
@param {ESLint.AST.Token | string} value
|
|
28
|
+
@returns {boolean}
|
|
29
|
+
*/
|
|
30
|
+
function isComment(value) {
|
|
31
|
+
return typeof value === 'object' && ['Line', 'Block'].includes(value.type);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
@param {ESLint.AST.Token | string} value
|
|
36
|
+
@returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
function isUnsafeNumericDotLeft(value) {
|
|
39
|
+
return typeof value === 'object' ? value.type === 'Numeric' && DECIMAL_INTEGER_LITERAL.test(value.value) : TRAILING_DECIMAL_INTEGER_LITERAL.test(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
Checks whether `left` and `right` can be placed directly next to each other without the last character of `left` and the first character of `right` merging into a token that changes the meaning of the code (for example two identifiers becoming one, `+` and `+` becoming `++`, or `2` and `.2` becoming `2.2`).
|
|
44
|
+
|
|
45
|
+
This is a lightweight, character-boundary check, not a full tokenizer, inspired by ESLint's own (more thorough) `canTokensBeAdjacent`: https://github.com/eslint/eslint/blob/b23015955c8d6e6516076190730f538c86927f26/lib/rules/utils/ast-utils.js#L2522-L2529
|
|
46
|
+
It only guards against the adjacency hazards that come up when building fixer replacement text in this codebase: identifiers/keywords/numbers merging, `+`/`-` doubling into `++`/`--`, a numeric literal absorbing a following decimal point, and a `/` being swallowed into a following comment. It intentionally doesn't try to detect hazards that require a real tokenizer, like an already-open `//` comment inside `left`, or malformed token text.
|
|
47
|
+
|
|
48
|
+
@param {ESLint.AST.Token | string} left - The left token or text.
|
|
49
|
+
@param {ESLint.AST.Token | string} right - The right token or text.
|
|
50
|
+
@returns {boolean} `false` if a space is needed between `left` and `right` to keep them from merging. `true` if they can safely be adjacent.
|
|
51
|
+
*/
|
|
52
|
+
export default function canTokensBeAdjacent(left, right) {
|
|
53
|
+
if (isLineCommentOrShebang(left)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const leftText = getText(left);
|
|
58
|
+
|
|
59
|
+
if (leftText === '') {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rightText = getText(right);
|
|
64
|
+
const lastCharacter = leftText.at(-1);
|
|
65
|
+
|
|
66
|
+
if (lastCharacter === '/' && isComment(right)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (rightText === '') {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const firstCharacter = rightText[0];
|
|
75
|
+
|
|
76
|
+
if (WORD_CHARACTER.test(lastCharacter) && WORD_CHARACTER.test(firstCharacter)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
(lastCharacter === '+' && firstCharacter === '+')
|
|
82
|
+
|| (lastCharacter === '-' && firstCharacter === '-')
|
|
83
|
+
) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
(isUnsafeNumericDotLeft(left) && firstCharacter === '.')
|
|
89
|
+
|| (TRAILING_DIGIT_DOT.test(leftText) && WORD_CHARACTER.test(firstCharacter))
|
|
90
|
+
) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return !(lastCharacter === '/' && (firstCharacter === '/' || firstCharacter === '*'));
|
|
95
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import quoteJsString from 'quote-js-string';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
Escape string and wrap the result in quotes.
|
|
@@ -14,11 +14,5 @@ export default function escapeString(string, quote = '\'') {
|
|
|
14
14
|
}
|
|
15
15
|
/* c8 ignore end */
|
|
16
16
|
|
|
17
|
-
return
|
|
18
|
-
quotes: quote === '"' ? 'double' : 'single',
|
|
19
|
-
wrap: true,
|
|
20
|
-
es6: true,
|
|
21
|
-
minimal: true,
|
|
22
|
-
lowercaseHex: false,
|
|
23
|
-
});
|
|
17
|
+
return quoteJsString(string, quote);
|
|
24
18
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import isIdentifier from 'is-identifier';
|
|
2
2
|
import resolveVariableName from './resolve-variable-name.js';
|
|
3
3
|
import getReferences from './get-references.js';
|
|
4
4
|
|
|
5
|
-
const {
|
|
6
|
-
isIdentifierName,
|
|
7
|
-
isStrictReservedWord,
|
|
8
|
-
isKeyword,
|
|
9
|
-
} = helperValidatorIdentifier;
|
|
10
|
-
|
|
11
5
|
// https://github.com/microsoft/TypeScript/issues/2536#issuecomment-87194347
|
|
12
6
|
const typescriptReservedWords = new Set([
|
|
13
7
|
'break',
|
|
@@ -72,14 +66,9 @@ const typescriptReservedWords = new Set([
|
|
|
72
66
|
'of',
|
|
73
67
|
]);
|
|
74
68
|
|
|
75
|
-
// Copied from https://github.com/babel/babel/blob/fce35af69101c6b316557e28abf60bdbf77d6a36/packages/babel-types/src/validators/isValidIdentifier.ts#L7
|
|
76
|
-
// Use this function instead of `require('@babel/types').isIdentifier`, since `@babel/helper-validator-identifier` package is much smaller
|
|
77
69
|
const isValidIdentifier = name =>
|
|
78
70
|
typeof name === 'string'
|
|
79
|
-
&&
|
|
80
|
-
&& !isStrictReservedWord(name, true)
|
|
81
|
-
&& isIdentifierName(name)
|
|
82
|
-
&& name !== 'arguments'
|
|
71
|
+
&& isIdentifier(name)
|
|
83
72
|
&& !typescriptReservedWords.has(name);
|
|
84
73
|
|
|
85
74
|
/*
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
|
|
6
|
+
// Copied from https://github.com/eslint/eslint/blob/b23015955c8d6e6516076190730f538c86927f26/lib/rules/utils/ast-utils.js#L1879-L1971
|
|
7
|
+
// and extended with TypeScript node types, following https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/util/getOperatorPrecedence.ts
|
|
8
|
+
|
|
9
|
+
const logicalOperatorPrecedence = {
|
|
10
|
+
'??': 4,
|
|
11
|
+
'||': 4,
|
|
12
|
+
'&&': 5,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const binaryOperatorPrecedence = {
|
|
16
|
+
'|': 6,
|
|
17
|
+
'^': 7,
|
|
18
|
+
'&': 8,
|
|
19
|
+
'==': 9,
|
|
20
|
+
'!=': 9,
|
|
21
|
+
'===': 9,
|
|
22
|
+
'!==': 9,
|
|
23
|
+
'<': 10,
|
|
24
|
+
'<=': 10,
|
|
25
|
+
'>': 10,
|
|
26
|
+
'>=': 10,
|
|
27
|
+
in: 10,
|
|
28
|
+
instanceof: 10,
|
|
29
|
+
'<<': 11,
|
|
30
|
+
'>>': 11,
|
|
31
|
+
'>>>': 11,
|
|
32
|
+
'+': 12,
|
|
33
|
+
'-': 12,
|
|
34
|
+
'*': 13,
|
|
35
|
+
'/': 13,
|
|
36
|
+
'%': 13,
|
|
37
|
+
'**': 15,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Precedence boundaries the `should-add-parentheses-to-*` helpers compare against. Exported so those
|
|
41
|
+
// callers can name the boundary instead of hardcoding a bare number that only makes sense against the
|
|
42
|
+
// table below.
|
|
43
|
+
// `UnaryExpression`, `AwaitExpression`, `TSTypeAssertion`, `TSNonNullExpression`, prefix `UpdateExpression`.
|
|
44
|
+
export const PRECEDENCE_UNARY = 16;
|
|
45
|
+
// `CallExpression`, `ChainExpression`, `ImportExpression`.
|
|
46
|
+
export const PRECEDENCE_CALL = 18;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
Get the operator precedence level of a given node. A higher number binds tighter.
|
|
50
|
+
|
|
51
|
+
This only covers node types that can appear as an operand of another expression (the kind of nodes these `should-add-parentheses-to-*` helpers compare against a surrounding operator); node types that can't (statements, patterns, etc) are not meaningful to compare and are not included.
|
|
52
|
+
|
|
53
|
+
This is only safe to use for a "is this operand's precedence lower than the precedence required by its new position" check (`getPrecedence(node) < requiredPrecedence`). It's not enough on its own to decide whether an operand needs parentheses *within a `BinaryExpression`/`LogicalExpression` of the same precedence class it already belongs to* - that also depends on associativity (`**` is right-associative, so `(a ** b) ** c` needs parentheses around the left operand even though both sides have the same precedence) and, for `LogicalExpression`, on whether `??` and `&&`/`||` are being mixed (a `SyntaxError` regardless of precedence, see `isMixedLogicalAndCoalesceExpressions` in ESLint's own `ast-utils.js`).
|
|
54
|
+
|
|
55
|
+
@param {ESTree.Node} node - The AST node to check.
|
|
56
|
+
@returns {number} The precedence level. Node types this function doesn't recognize (for example `Identifier`, `Literal`, or `MemberExpression`) get the highest level, since they never need to be parenthesized based on precedence alone.
|
|
57
|
+
*/
|
|
58
|
+
export default function getPrecedence(node) {
|
|
59
|
+
switch (node.type) {
|
|
60
|
+
case 'SequenceExpression': {
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 'AssignmentExpression':
|
|
65
|
+
case 'ArrowFunctionExpression':
|
|
66
|
+
case 'YieldExpression': {
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case 'ConditionalExpression': {
|
|
71
|
+
return 3;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case 'LogicalExpression': {
|
|
75
|
+
return logicalOperatorPrecedence[node.operator];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
case 'BinaryExpression': {
|
|
79
|
+
return binaryOperatorPrecedence[node.operator];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'TSAsExpression':
|
|
83
|
+
case 'TSSatisfiesExpression': {
|
|
84
|
+
return 10;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
case 'UnaryExpression':
|
|
88
|
+
case 'AwaitExpression':
|
|
89
|
+
case 'TSTypeAssertion':
|
|
90
|
+
case 'TSNonNullExpression': {
|
|
91
|
+
return PRECEDENCE_UNARY;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case 'UpdateExpression': {
|
|
95
|
+
return node.prefix ? PRECEDENCE_UNARY : 17;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case 'CallExpression':
|
|
99
|
+
case 'ChainExpression':
|
|
100
|
+
case 'ImportExpression': {
|
|
101
|
+
return PRECEDENCE_CALL;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'NewExpression': {
|
|
105
|
+
return 19;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
default: {
|
|
109
|
+
return 20;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
@param {import('estree').Node} node
|
|
7
7
|
@returns {import('eslint').SourceCode}
|
|
8
8
|
*/
|
|
9
|
-
function getTokenStore(context, node) {
|
|
9
|
+
export default function getTokenStore(context, node) {
|
|
10
10
|
const {sourceCode} = context;
|
|
11
11
|
|
|
12
12
|
if (
|
|
@@ -20,5 +20,3 @@ function getTokenStore(context, node) {
|
|
|
20
20
|
|
|
21
21
|
return sourceCode;
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
export default getTokenStore;
|
|
@@ -25,6 +25,9 @@ const mergeTraceMap = (target, source) => {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export class GlobalReferenceTracker {
|
|
28
|
+
static READ = ReferenceTracker.READ;
|
|
29
|
+
static CALL = ReferenceTracker.CALL;
|
|
30
|
+
static CONSTRUCT = ReferenceTracker.CONSTRUCT;
|
|
28
31
|
#traceMap = {};
|
|
29
32
|
#context;
|
|
30
33
|
#filter;
|
|
@@ -73,9 +76,3 @@ export class GlobalReferenceTracker {
|
|
|
73
76
|
);
|
|
74
77
|
}
|
|
75
78
|
}
|
|
76
|
-
|
|
77
|
-
Object.assign(GlobalReferenceTracker, {
|
|
78
|
-
READ: ReferenceTracker.READ,
|
|
79
|
-
CALL: ReferenceTracker.CALL,
|
|
80
|
-
CONSTRUCT: ReferenceTracker.CONSTRUCT,
|
|
81
|
-
});
|
package/rules/utils/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export {
|
|
|
23
23
|
} from './boolean.js';
|
|
24
24
|
|
|
25
25
|
export {default as assertToken} from './assert-token.js';
|
|
26
|
+
export {default as canTokensBeAdjacent} from './can-tokens-be-adjacent.js';
|
|
26
27
|
export {default as cartesianProductSamples} from './cartesian-product-samples.js';
|
|
27
28
|
export {default as containsSuspensionPoint} from './contains-suspension-point.js';
|
|
28
29
|
export {default as escapeString} from './escape-string.js';
|
|
@@ -32,6 +33,7 @@ export {default as getCallExpressionArgumentsText} from './get-call-expression-a
|
|
|
32
33
|
export {getCallExpressionTokens, getNewExpressionTokens} from './get-call-or-new-expression-tokens.js';
|
|
33
34
|
export {default as getDuplicateArrayElements, isComparableStaticValue} from './get-duplicate-array-elements.js';
|
|
34
35
|
export {default as getIndentString} from './get-indent-string.js';
|
|
36
|
+
export {default as isIdentifierName} from './is-identifier-name.js';
|
|
35
37
|
export {default as getComments} from './get-comments.js';
|
|
36
38
|
export {
|
|
37
39
|
getLastTrailingCommentOnSameLine,
|
|
@@ -94,6 +96,7 @@ export {default as isNodeValueNotFunction} from './is-node-value-not-function.js
|
|
|
94
96
|
export {default as isNodeContainsLexicalThis} from './is-node-contains-lexical-this.js';
|
|
95
97
|
export {default as isOnSameLine} from './is-on-same-line.js';
|
|
96
98
|
export {default as isPromiseType} from './is-promise-type.js';
|
|
99
|
+
export {default as isReactHookName} from './is-react-hook-name.js';
|
|
97
100
|
export {default as isSameIdentifier} from './is-same-identifier.js';
|
|
98
101
|
export {default as isSameReference} from './is-same-reference.js';
|
|
99
102
|
export {default as isUnresolvedVariable} from './is-unresolved-variable.js';
|
|
@@ -133,6 +136,7 @@ export {
|
|
|
133
136
|
isBuiltinSet,
|
|
134
137
|
} from './builtin-collection-type.js';
|
|
135
138
|
export {checkVueTemplate} from './rule.js';
|
|
139
|
+
export {default as getPrecedence} from './get-precedence.js';
|
|
136
140
|
export {default as shouldAddParenthesesToAwaitExpressionArgument} from './should-add-parentheses-to-await-expression-argument.js';
|
|
137
141
|
export {default as shouldAddParenthesesToCallExpressionCallee} from './should-add-parentheses-to-call-expression-callee.js';
|
|
138
142
|
export {default as shouldAddParenthesesToConditionalExpressionChild} from './should-add-parentheses-to-conditional-expression-child.js';
|