eslint-plugin-unicorn 65.0.1 → 67.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/core-rule-replacements.js +9 -0
- package/index.js +11 -7
- package/package.json +34 -30
- package/readme.md +323 -212
- package/rules/ast/call-or-new-expression.js +1 -1
- package/rules/ast/index.js +4 -0
- package/rules/ast/is-loop.js +5 -0
- package/rules/ast/literal.js +18 -0
- package/rules/ast/loop-types.js +9 -0
- package/rules/better-dom-traversing.js +46 -16
- package/rules/catch-error-name.js +3 -0
- package/rules/class-reference-in-static-methods.js +451 -0
- package/rules/comment-content.js +1083 -0
- package/rules/consistent-assert.js +3 -0
- package/rules/consistent-boolean-name.js +493 -0
- package/rules/consistent-class-member-order.js +268 -0
- package/rules/consistent-compound-words.js +40 -26
- package/rules/consistent-date-clone.js +3 -0
- package/rules/consistent-destructuring.js +267 -143
- package/rules/consistent-empty-array-spread.js +3 -0
- package/rules/consistent-existence-index-check.js +3 -0
- package/rules/consistent-export-decorator-position.js +206 -0
- package/rules/consistent-function-scoping.js +79 -33
- package/rules/consistent-function-style.js +344 -0
- package/rules/consistent-json-file-read.js +4 -1
- package/rules/consistent-optional-chaining.js +203 -0
- package/rules/consistent-template-literal-escape.js +4 -1
- package/rules/custom-error-definition.js +222 -20
- package/rules/dom-node-dataset.js +234 -47
- package/rules/empty-brace-spaces.js +3 -0
- package/rules/error-message.js +3 -0
- package/rules/escape-case.js +3 -0
- package/rules/expiring-todo-comments.js +118 -19
- package/rules/explicit-length-check.js +20 -26
- package/rules/explicit-timer-delay.js +156 -0
- package/rules/filename-case.js +101 -12
- package/rules/fix/index.js +3 -2
- package/rules/fix/remove-argument.js +18 -5
- package/rules/fix/remove-spaces-after.js +3 -4
- package/rules/fix/{remove-expression-statement.js → remove-statement.js} +20 -7
- package/rules/fix/replace-node-or-token-and-spaces-before.js +1 -1
- package/rules/fix/replace-node-with-expression.js +47 -0
- package/rules/fix/replace-reference-identifier.js +4 -1
- package/rules/id-match.js +77 -0
- package/rules/import-style.js +25 -29
- package/rules/index.js +91 -2
- package/rules/isolated-functions.js +7 -4
- package/rules/logical-assignment-operators.js +112 -0
- package/rules/max-nested-calls.js +102 -0
- package/rules/new-for-builtins.js +14 -7
- package/rules/no-abusive-eslint-disable.js +5 -1
- package/rules/no-accessor-recursion.js +3 -0
- package/rules/no-anonymous-default-export.js +7 -3
- package/rules/no-array-callback-reference.js +148 -2
- package/rules/no-array-fill-with-reference-type.js +4 -13
- package/rules/no-array-from-fill.js +3 -0
- package/rules/no-array-method-this-argument.js +45 -20
- package/rules/no-array-reduce.js +82 -9
- package/rules/no-array-splice.js +232 -0
- package/rules/no-asterisk-prefix-in-documentation-comments.js +89 -0
- package/rules/no-await-expression-member.js +3 -0
- package/rules/no-await-in-promise-methods.js +3 -0
- package/rules/no-blob-to-file.js +9 -44
- package/rules/no-break-in-nested-loop.js +97 -0
- package/rules/no-canvas-to-image.js +3 -0
- package/rules/no-computed-property-existence-check.js +164 -0
- package/rules/no-confusing-array-splice.js +41 -13
- package/rules/no-confusing-array-with.js +103 -0
- package/rules/no-console-spaces.js +3 -0
- package/rules/no-declarations-before-early-exit.js +276 -0
- package/rules/no-document-cookie.js +3 -0
- package/rules/no-duplicate-loops.js +60 -0
- package/rules/no-duplicate-set-values.js +3 -0
- package/rules/no-empty-file.js +109 -3
- package/rules/no-error-property-assignment.js +440 -0
- package/rules/no-exports-in-scripts.js +3 -0
- package/rules/{no-array-for-each.js → no-for-each.js} +21 -11
- package/rules/no-for-loop.js +557 -96
- package/rules/no-global-object-property-assignment.js +89 -0
- package/rules/no-immediate-mutation.js +5 -2
- package/rules/no-incorrect-query-selector.js +8 -36
- package/rules/no-incorrect-template-string-interpolation.js +93 -0
- package/rules/no-instanceof-builtins.js +4 -1
- package/rules/no-invalid-argument-count.js +1345 -0
- package/rules/no-invalid-fetch-options.js +6 -3
- package/rules/no-invalid-file-input-accept.js +12 -5
- package/rules/no-invalid-remove-event-listener.js +3 -0
- package/rules/no-keyword-prefix.js +27 -22
- package/rules/no-late-current-target-access.js +3 -0
- package/rules/no-lonely-if.js +3 -0
- package/rules/no-magic-array-flat-depth.js +3 -0
- package/rules/no-manually-wrapped-comments.js +20 -9
- package/rules/no-mismatched-map-key.js +520 -0
- package/rules/no-named-default.js +3 -0
- package/rules/no-negated-array-predicate.js +212 -0
- package/rules/no-negated-comparison.js +347 -0
- package/rules/no-negated-condition.js +3 -0
- package/rules/no-negation-in-equality-check.js +3 -0
- package/rules/no-nested-ternary.js +3 -0
- package/rules/no-new-array.js +3 -0
- package/rules/no-new-buffer.js +3 -0
- package/rules/no-non-function-verb-prefix.js +324 -0
- package/rules/no-null.js +3 -0
- package/rules/no-object-as-default-parameter.js +3 -0
- package/rules/no-object-methods-with-collections.js +381 -0
- package/rules/no-optional-chaining-on-undeclared-variable.js +135 -0
- package/rules/no-process-exit.js +3 -0
- package/rules/no-redundant-comparison.js +305 -0
- package/rules/no-return-array-push.js +164 -0
- package/rules/no-single-promise-in-promise-methods.js +95 -5
- package/rules/no-static-only-class.js +3 -0
- package/rules/no-subtraction-comparison.js +111 -0
- package/rules/no-thenable.js +3 -0
- package/rules/no-this-assignment.js +3 -0
- package/rules/no-this-outside-of-class.js +3 -0
- package/rules/no-top-level-assignment-in-function.js +76 -0
- package/rules/no-top-level-side-effects.js +74 -0
- package/rules/no-typeof-undefined.js +5 -3
- package/rules/no-uncalled-method.js +308 -0
- package/rules/no-undeclared-class-members.js +349 -0
- package/rules/no-unnecessary-array-flat-depth.js +3 -0
- package/rules/no-unnecessary-array-splice-count.js +3 -0
- package/rules/no-unnecessary-await.js +3 -0
- package/rules/no-unnecessary-global-this.js +176 -0
- package/rules/no-unnecessary-nested-ternary.js +3 -0
- package/rules/no-unnecessary-polyfills.js +29 -11
- package/rules/no-unnecessary-slice-end.js +3 -0
- package/rules/no-unnecessary-splice.js +157 -0
- package/rules/no-unreadable-array-destructuring.js +99 -9
- package/rules/no-unreadable-for-of-expression.js +130 -0
- package/rules/no-unreadable-iife.js +3 -0
- package/rules/no-unreadable-new-expression.js +63 -0
- package/rules/no-unreadable-object-destructuring.js +140 -0
- package/rules/no-unsafe-buffer-conversion.js +532 -0
- package/rules/no-unsafe-dom-html.js +172 -0
- package/rules/no-unsafe-property-key.js +584 -0
- package/rules/no-unsafe-string-replacement.js +78 -0
- package/rules/no-unused-array-method-return.js +4 -1
- package/rules/no-unused-properties.js +71 -35
- package/rules/no-useless-boolean-cast.js +120 -0
- package/rules/no-useless-coercion.js +91 -0
- package/rules/no-useless-collection-argument.js +3 -0
- package/rules/no-useless-concat.js +144 -0
- package/rules/no-useless-continue.js +81 -0
- package/rules/no-useless-else.js +269 -0
- package/rules/no-useless-error-capture-stack-trace.js +3 -0
- package/rules/no-useless-fallback-in-spread.js +125 -1
- package/rules/no-useless-iterator-to-array.js +3 -0
- package/rules/no-useless-length-check.js +20 -8
- package/rules/no-useless-override.js +217 -0
- package/rules/no-useless-promise-resolve-reject.js +6 -1
- package/rules/no-useless-recursion.js +118 -0
- package/rules/no-useless-spread.js +189 -73
- package/rules/no-useless-switch-case.js +12 -1
- package/rules/no-useless-template-literals.js +370 -0
- package/rules/no-useless-undefined.js +254 -21
- package/rules/no-zero-fractions.js +3 -0
- package/rules/number-literal-case.js +3 -0
- package/rules/numeric-separators-style.js +12 -11
- package/rules/operator-assignment.js +111 -0
- package/rules/prefer-add-event-listener-options.js +54 -0
- package/rules/prefer-add-event-listener.js +3 -0
- package/rules/prefer-array-find.js +16 -11
- package/rules/prefer-array-flat-map.js +180 -12
- package/rules/prefer-array-flat.js +16 -11
- package/rules/prefer-array-from-map.js +465 -0
- package/rules/prefer-array-index-of.js +3 -0
- package/rules/prefer-array-last-methods.js +4 -1
- package/rules/prefer-array-slice.js +97 -0
- package/rules/prefer-array-some.js +24 -4
- package/rules/prefer-at.js +94 -36
- package/rules/prefer-await.js +69 -0
- package/rules/prefer-bigint-literals.js +4 -0
- package/rules/prefer-blob-reading-methods.js +3 -0
- package/rules/prefer-class-fields.js +3 -0
- package/rules/prefer-classlist-toggle.js +5 -2
- package/rules/prefer-code-point.js +3 -0
- package/rules/prefer-date-now.js +3 -0
- package/rules/prefer-default-parameters.js +7 -4
- package/rules/prefer-direct-iteration.js +424 -0
- package/rules/prefer-dispose.js +346 -0
- package/rules/prefer-dom-node-append.js +3 -0
- package/rules/prefer-dom-node-html-methods.js +136 -0
- package/rules/prefer-dom-node-remove.js +3 -0
- package/rules/prefer-dom-node-text-content.js +26 -0
- package/rules/prefer-early-return.js +267 -0
- package/rules/prefer-else-if.js +524 -0
- package/rules/prefer-event-target.js +3 -0
- package/rules/prefer-export-from.js +5 -3
- package/rules/prefer-get-or-insert-computed.js +3 -0
- package/rules/prefer-global-number-constants.js +86 -0
- package/rules/prefer-global-this.js +35 -19
- package/rules/prefer-has-check.js +946 -0
- package/rules/prefer-https.js +55 -11
- package/rules/prefer-identifier-import-export-specifiers.js +94 -0
- package/rules/prefer-import-meta-properties.js +13 -10
- package/rules/prefer-includes-over-repeated-comparisons.js +10 -71
- package/rules/prefer-includes.js +4 -0
- package/rules/prefer-iterable-in-constructor.js +346 -0
- package/rules/prefer-iterator-concat.js +3 -0
- package/rules/prefer-iterator-to-array-at-end.js +3 -0
- package/rules/prefer-iterator-to-array.js +210 -0
- package/rules/prefer-keyboard-event-key.js +7 -3
- package/rules/prefer-location-assign.js +145 -0
- package/rules/prefer-logical-operator-over-ternary.js +253 -2
- package/rules/prefer-map-from-entries.js +482 -0
- package/rules/prefer-math-abs.js +3 -0
- package/rules/prefer-math-min-max.js +3 -0
- package/rules/prefer-math-trunc.js +100 -2
- package/rules/prefer-minimal-ternary.js +243 -0
- package/rules/prefer-modern-dom-apis.js +181 -16
- package/rules/prefer-modern-math-apis.js +3 -0
- package/rules/prefer-module.js +5 -1
- package/rules/prefer-native-coercion-functions.js +5 -4
- package/rules/prefer-negative-index.js +4 -12
- package/rules/prefer-node-protocol.js +3 -0
- package/rules/prefer-number-coercion.js +157 -0
- package/rules/prefer-number-is-safe-integer.js +193 -0
- package/rules/prefer-number-properties.js +71 -9
- package/rules/prefer-object-define-properties.js +208 -0
- package/rules/prefer-object-destructuring-defaults.js +177 -0
- package/rules/prefer-object-from-entries.js +141 -2
- package/rules/prefer-object-iterable-methods.js +609 -0
- package/rules/prefer-optional-catch-binding.js +3 -0
- package/rules/prefer-path2d.js +842 -0
- package/rules/prefer-private-class-fields.js +503 -0
- package/rules/prefer-prototype-methods.js +3 -0
- package/rules/prefer-query-selector.js +74 -32
- package/rules/prefer-queue-microtask.js +7 -12
- package/rules/prefer-reflect-apply.js +3 -0
- package/rules/prefer-regexp-test.js +489 -84
- package/rules/prefer-response-static-json.js +3 -0
- package/rules/prefer-scoped-selector.js +189 -0
- package/rules/prefer-set-has.js +8 -14
- package/rules/prefer-set-size.js +6 -34
- package/rules/prefer-short-arrow-method.js +138 -0
- package/rules/prefer-simple-condition-first.js +5 -4
- package/rules/prefer-simple-sort-comparator.js +270 -0
- package/rules/prefer-single-array-predicate.js +209 -0
- package/rules/prefer-single-call.js +3 -0
- package/rules/prefer-single-object-destructuring.js +145 -0
- package/rules/prefer-smaller-scope.js +238 -0
- package/rules/prefer-split-limit.js +4 -1
- package/rules/prefer-spread.js +915 -86
- package/rules/prefer-string-match-all.js +4 -1
- package/rules/prefer-string-pad-start-end.js +4 -3
- package/rules/prefer-string-raw.js +4 -1
- package/rules/prefer-string-repeat.js +3 -0
- package/rules/prefer-string-replace-all.js +131 -65
- package/rules/prefer-string-slice.js +21 -5
- package/rules/prefer-string-starts-ends-with.js +84 -11
- package/rules/prefer-string-trim-start-end.js +9 -0
- package/rules/prefer-structured-clone.js +3 -0
- package/rules/prefer-switch.js +9 -5
- package/rules/prefer-temporal.js +254 -0
- package/rules/prefer-ternary.js +146 -111
- package/rules/prefer-top-level-await.js +3 -0
- package/rules/prefer-type-error.js +31 -9
- package/rules/prefer-type-literal-last.js +139 -0
- package/rules/prefer-uint8array-base64.js +165 -0
- package/rules/prefer-unicode-code-point-escapes.js +400 -0
- package/rules/prefer-url-href.js +592 -0
- package/rules/prevent-abbreviations.js +28 -24
- package/rules/relative-url-style.js +6 -1
- package/rules/require-array-join-separator.js +9 -1
- package/rules/require-array-sort-compare.js +88 -0
- package/rules/require-css-escape.js +3 -0
- package/rules/require-module-attributes.js +3 -0
- package/rules/require-module-specifiers.js +4 -2
- package/rules/require-number-to-fixed-digits-argument.js +3 -0
- package/rules/require-passive-events.js +4 -5
- package/rules/require-post-message-target-origin.js +3 -0
- package/rules/require-proxy-trap-boolean-return.js +415 -0
- package/rules/rule/to-eslint-rule.js +1 -1
- package/rules/shared/abbreviations.js +2 -0
- package/rules/shared/iterator-helpers.js +104 -0
- package/rules/shared/negative-index.js +13 -4
- package/rules/shared/no-array-mutate-rule.js +9 -1
- package/rules/shared/simple-array-search-rule.js +135 -3
- package/rules/shared/splice-replacements.js +149 -0
- package/rules/shared/substring.js +69 -0
- package/rules/string-content.js +7 -3
- package/rules/switch-case-braces.js +102 -11
- package/rules/switch-case-break-position.js +5 -3
- package/rules/template-indent.js +3 -0
- package/rules/text-encoding-identifier-case.js +77 -0
- package/rules/throw-new-error.js +3 -0
- package/rules/try-complexity.js +3 -0
- package/rules/utils/boolean.js +27 -11
- package/rules/utils/cartesian-product-samples.js +1 -1
- package/rules/utils/comments.js +47 -0
- package/rules/utils/comparison.js +80 -0
- package/rules/utils/escape-template-element-raw.js +1 -1
- package/rules/utils/get-available-variable-name.js +1 -1
- package/rules/utils/get-comments.js +15 -0
- package/rules/utils/has-unsafe-arrow-conversion-reference.js +46 -0
- package/rules/utils/imports.js +11 -0
- package/rules/utils/index.js +43 -1
- package/rules/utils/is-array.js +79 -0
- package/rules/utils/is-bigint.js +86 -0
- package/rules/utils/is-boolean.js +564 -0
- package/rules/utils/is-dom-node.js +53 -0
- package/rules/utils/is-global-identifier.js +13 -0
- package/rules/utils/is-left-hand-side.js +1 -0
- package/rules/utils/is-map.js +22 -0
- package/rules/utils/is-number.js +51 -14
- package/rules/utils/is-promise-type.js +28 -0
- package/rules/utils/is-reg-exp.js +24 -0
- package/rules/utils/is-same-reference.js +2 -1
- package/rules/utils/is-set.js +22 -0
- package/rules/utils/is-string.js +119 -0
- package/rules/utils/is-strong-precedence-node.js +32 -0
- package/rules/utils/is-typescript-file.js +9 -0
- package/rules/utils/is-virtual-filename.js +9 -0
- package/rules/utils/normalize-comment.js +38 -0
- package/rules/utils/numeric.js +1 -1
- package/rules/utils/on-root.js +31 -0
- package/rules/utils/rule.js +2 -6
- package/rules/utils/scope.js +22 -0
- package/rules/utils/should-skip-known-non-array-receiver.js +13 -0
- package/rules/utils/type-check.js +20 -0
- package/rules/utils/type-helpers.js +595 -0
- package/rules/utils/types.js +27 -0
- package/rules/utils/unwrap-typescript-expression.js +16 -0
- package/rules/no-hex-escape.js +0 -55
package/rules/prefer-at.js
CHANGED
|
@@ -12,11 +12,14 @@ import {
|
|
|
12
12
|
needsSemicolon,
|
|
13
13
|
shouldAddParenthesesToMemberExpressionObject,
|
|
14
14
|
isLeftHandSide,
|
|
15
|
+
isString,
|
|
16
|
+
unwrapTypeScriptExpression as unwrapExpression,
|
|
15
17
|
} from './utils/index.js';
|
|
16
18
|
import {
|
|
17
19
|
getNegativeIndexLengthNode,
|
|
18
20
|
removeLengthNode,
|
|
19
21
|
} from './shared/negative-index.js';
|
|
22
|
+
import {getSubstringSingleCharacterIndex} from './shared/substring.js';
|
|
20
23
|
import {removeMemberExpressionProperty, removeMethodCall} from './fix/index.js';
|
|
21
24
|
import {
|
|
22
25
|
isLiteral,
|
|
@@ -30,6 +33,7 @@ const MESSAGE_ID_NEGATIVE_INDEX = 'negative-index';
|
|
|
30
33
|
const MESSAGE_ID_INDEX = 'index';
|
|
31
34
|
const MESSAGE_ID_STRING_CHAR_AT_NEGATIVE = 'string-char-at-negative';
|
|
32
35
|
const MESSAGE_ID_STRING_CHAR_AT = 'string-char-at';
|
|
36
|
+
const MESSAGE_ID_STRING_SUBSTRING = 'string-substring';
|
|
33
37
|
const MESSAGE_ID_SLICE = 'slice';
|
|
34
38
|
const MESSAGE_ID_GET_LAST_FUNCTION = 'get-last-function';
|
|
35
39
|
const SUGGESTION_ID = 'use-at';
|
|
@@ -38,6 +42,7 @@ const messages = {
|
|
|
38
42
|
[MESSAGE_ID_INDEX]: 'Prefer `.at(…)` over index access.',
|
|
39
43
|
[MESSAGE_ID_STRING_CHAR_AT_NEGATIVE]: 'Prefer `String#at(…)` over `String#charAt(….length - index)`.',
|
|
40
44
|
[MESSAGE_ID_STRING_CHAR_AT]: 'Prefer `String#at(…)` over `String#charAt(…)`.',
|
|
45
|
+
[MESSAGE_ID_STRING_SUBSTRING]: 'Prefer `String#at(…)` over `String#substring(…)` when getting one character.',
|
|
41
46
|
[MESSAGE_ID_SLICE]: 'Prefer `.at(…)` over the first element from `.slice(…)`.',
|
|
42
47
|
[MESSAGE_ID_GET_LAST_FUNCTION]: 'Prefer `.at(-1)` over `{{description}}(…)` to get the last element.',
|
|
43
48
|
[SUGGESTION_ID]: 'Use `.at(…)`.',
|
|
@@ -45,21 +50,6 @@ const messages = {
|
|
|
45
50
|
|
|
46
51
|
const isArguments = node => node.type === 'Identifier' && node.name === 'arguments';
|
|
47
52
|
|
|
48
|
-
function unwrapExpression(node) {
|
|
49
|
-
if (
|
|
50
|
-
[
|
|
51
|
-
'TSAsExpression',
|
|
52
|
-
'TSTypeAssertion',
|
|
53
|
-
'TSNonNullExpression',
|
|
54
|
-
'TSSatisfiesExpression',
|
|
55
|
-
].includes(node.type)
|
|
56
|
-
) {
|
|
57
|
-
return unwrapExpression(node.expression);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return node;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
53
|
const isUnsupportedAtReceiverExpression = node => {
|
|
64
54
|
node = unwrapExpression(node);
|
|
65
55
|
|
|
@@ -109,7 +99,7 @@ const isLiteralNegativeInteger = node =>
|
|
|
109
99
|
&& node.prefix
|
|
110
100
|
&& node.operator === '-'
|
|
111
101
|
&& node.argument.type === 'Literal'
|
|
112
|
-
&& Number.
|
|
102
|
+
&& Number.isSafeInteger(node.argument.value)
|
|
113
103
|
&& node.argument.value > 0;
|
|
114
104
|
const isZeroIndexAccess = node =>
|
|
115
105
|
isMemberExpression(node.parent, {
|
|
@@ -131,7 +121,21 @@ const isArrayPopOrShiftCall = (node, method) =>
|
|
|
131
121
|
const isArrayPopCall = node => isArrayPopOrShiftCall(node, 'pop');
|
|
132
122
|
const isArrayShiftCall = node => isArrayPopOrShiftCall(node, 'shift');
|
|
133
123
|
|
|
134
|
-
function
|
|
124
|
+
function getFirstElementGetMethod(node) {
|
|
125
|
+
if (isZeroIndexAccess(node)) {
|
|
126
|
+
return isLeftHandSide(node.parent) ? undefined : 'zero-index';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (isArrayShiftCall(node)) {
|
|
130
|
+
return 'shift';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (isArrayPopCall(node)) {
|
|
134
|
+
return 'pop';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function getSliceCallResult(node) {
|
|
135
139
|
const sliceArgumentsLength = node.arguments.length;
|
|
136
140
|
const [startIndexNode, endIndexNode] = node.arguments;
|
|
137
141
|
|
|
@@ -139,19 +143,7 @@ function checkSliceCall(node) {
|
|
|
139
143
|
return;
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
if (isZeroIndexAccess(node)) {
|
|
144
|
-
if (isLeftHandSide(node.parent)) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
firstElementGetMethod = 'zero-index';
|
|
149
|
-
} else if (isArrayShiftCall(node)) {
|
|
150
|
-
firstElementGetMethod = 'shift';
|
|
151
|
-
} else if (isArrayPopCall(node)) {
|
|
152
|
-
firstElementGetMethod = 'pop';
|
|
153
|
-
}
|
|
154
|
-
|
|
146
|
+
const firstElementGetMethod = getFirstElementGetMethod(node);
|
|
155
147
|
if (!firstElementGetMethod) {
|
|
156
148
|
return;
|
|
157
149
|
}
|
|
@@ -192,6 +184,27 @@ const lodashLastFunctions = [
|
|
|
192
184
|
'underscore.last',
|
|
193
185
|
];
|
|
194
186
|
|
|
187
|
+
// TODO: Remove this once DOM collections like NodeList and HTMLCollection reliably support `.at()` in browsers.
|
|
188
|
+
const domCollectionProperties = [
|
|
189
|
+
'childNodes',
|
|
190
|
+
'children',
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
const domCollectionMethods = [
|
|
194
|
+
'getElementsByClassName',
|
|
195
|
+
'getElementsByName',
|
|
196
|
+
'getElementsByTagName',
|
|
197
|
+
'getElementsByTagNameNS',
|
|
198
|
+
'querySelectorAll',
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const isDomCollectionReceiver = node => {
|
|
202
|
+
node = unwrapExpression(node);
|
|
203
|
+
|
|
204
|
+
return isMemberExpression(node, {properties: domCollectionProperties})
|
|
205
|
+
|| isMethodCall(node, {methods: domCollectionMethods});
|
|
206
|
+
};
|
|
207
|
+
|
|
195
208
|
/** @param {import('eslint').Rule.RuleContext} context */
|
|
196
209
|
function create(context) {
|
|
197
210
|
const {
|
|
@@ -210,17 +223,21 @@ function create(context) {
|
|
|
210
223
|
return;
|
|
211
224
|
}
|
|
212
225
|
|
|
226
|
+
if (isDomCollectionReceiver(node.object)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
213
230
|
const indexNode = node.property;
|
|
214
231
|
const lengthNode = getNegativeIndexLengthNode(indexNode, node.object);
|
|
215
232
|
|
|
216
233
|
if (!lengthNode) {
|
|
217
|
-
if
|
|
234
|
+
// Only if we are sure it's a positive integer
|
|
235
|
+
const staticValue = getStaticValue(indexNode, sourceCode.getScope(indexNode));
|
|
236
|
+
if (!staticValue || !Number.isSafeInteger(staticValue.value) || staticValue.value < 0) {
|
|
218
237
|
return;
|
|
219
238
|
}
|
|
220
239
|
|
|
221
|
-
|
|
222
|
-
const staticValue = getStaticValue(indexNode, sourceCode.getScope(indexNode));
|
|
223
|
-
if (!staticValue || !Number.isInteger(staticValue.value) || staticValue.value < 0) {
|
|
240
|
+
if (!checkAllIndexAccess && !isString(node.object, context)) {
|
|
224
241
|
return;
|
|
225
242
|
}
|
|
226
243
|
|
|
@@ -304,6 +321,44 @@ function create(context) {
|
|
|
304
321
|
};
|
|
305
322
|
});
|
|
306
323
|
|
|
324
|
+
// `string.substring(index, index + 1)`
|
|
325
|
+
context.on('CallExpression', node => {
|
|
326
|
+
const indexNode = getSubstringSingleCharacterIndex(node);
|
|
327
|
+
if (!indexNode) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const problem = {
|
|
332
|
+
node,
|
|
333
|
+
messageId: MESSAGE_ID_STRING_SUBSTRING,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const [firstArgument, secondArgument] = node.arguments;
|
|
337
|
+
const replacementRange = [
|
|
338
|
+
getParenthesizedRange(firstArgument, context)[0],
|
|
339
|
+
getParenthesizedRange(secondArgument, context)[1],
|
|
340
|
+
];
|
|
341
|
+
const hasCommentsInsideReplacementRange = sourceCode.getAllComments().some(comment => {
|
|
342
|
+
const commentRange = sourceCode.getRange(comment);
|
|
343
|
+
return commentRange[0] >= replacementRange[0] && commentRange[1] <= replacementRange[1];
|
|
344
|
+
});
|
|
345
|
+
if (hasCommentsInsideReplacementRange) {
|
|
346
|
+
return problem;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
...problem,
|
|
351
|
+
suggest: [{
|
|
352
|
+
messageId: SUGGESTION_ID,
|
|
353
|
+
* fix(fixer) {
|
|
354
|
+
yield fixer.replaceText(node.callee.property, 'at');
|
|
355
|
+
|
|
356
|
+
yield fixer.replaceTextRange(replacementRange, getParenthesizedText(indexNode, context));
|
|
357
|
+
},
|
|
358
|
+
}],
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
|
|
307
362
|
// `.slice()`
|
|
308
363
|
context.on('CallExpression', sliceCall => {
|
|
309
364
|
if (!isMethodCall(sliceCall, {
|
|
@@ -315,7 +370,7 @@ function create(context) {
|
|
|
315
370
|
return;
|
|
316
371
|
}
|
|
317
372
|
|
|
318
|
-
const result =
|
|
373
|
+
const result = getSliceCallResult(sliceCall);
|
|
319
374
|
if (!result) {
|
|
320
375
|
return;
|
|
321
376
|
}
|
|
@@ -386,7 +441,7 @@ function create(context) {
|
|
|
386
441
|
fixed = `(${fixed})`;
|
|
387
442
|
}
|
|
388
443
|
|
|
389
|
-
fixed
|
|
444
|
+
fixed += '.at(-1)';
|
|
390
445
|
|
|
391
446
|
const tokenBefore = sourceCode.getTokenBefore(node);
|
|
392
447
|
if (needsSemicolon(tokenBefore, context, fixed)) {
|
|
@@ -431,6 +486,9 @@ const config = {
|
|
|
431
486
|
schema,
|
|
432
487
|
defaultOptions: [{getLastElementFunctions: [], checkAllIndexAccess: false}],
|
|
433
488
|
messages,
|
|
489
|
+
languages: [
|
|
490
|
+
'js/js',
|
|
491
|
+
],
|
|
434
492
|
},
|
|
435
493
|
};
|
|
436
494
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {getPropertyName} from '@eslint-community/eslint-utils';
|
|
2
|
+
import {isPromiseType} from './utils/index.js';
|
|
3
|
+
|
|
4
|
+
const MESSAGE_ID = 'prefer-await';
|
|
5
|
+
const messages = {
|
|
6
|
+
[MESSAGE_ID]: 'Prefer `await` over promise chaining with `.{{method}}()`.',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const promiseMethods = new Set(['then', 'catch', 'finally']);
|
|
10
|
+
|
|
11
|
+
function isPromiseObject(node, context) {
|
|
12
|
+
const {parserServices} = context.sourceCode;
|
|
13
|
+
if (!parserServices?.program) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
return isPromiseType(
|
|
19
|
+
parserServices.getTypeAtLocation(node),
|
|
20
|
+
parserServices.program.getTypeChecker(),
|
|
21
|
+
);
|
|
22
|
+
} catch {
|
|
23
|
+
// TypeScript can throw while resolving incomplete projects; keep this rule best-effort.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @param {import('eslint').Rule.RuleContext} context */
|
|
28
|
+
const create = context => {
|
|
29
|
+
context.on('CallExpression', callExpression => {
|
|
30
|
+
const {callee} = callExpression;
|
|
31
|
+
if (callee.type !== 'MemberExpression') {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const method = getPropertyName(callee, context.sourceCode.getScope(callExpression));
|
|
36
|
+
if (!promiseMethods.has(method)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const promiseObject = isPromiseObject(callee.object, context);
|
|
41
|
+
if (promiseObject === false) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
node: callee.property,
|
|
47
|
+
messageId: MESSAGE_ID,
|
|
48
|
+
data: {method},
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
54
|
+
const config = {
|
|
55
|
+
create,
|
|
56
|
+
meta: {
|
|
57
|
+
type: 'suggestion',
|
|
58
|
+
docs: {
|
|
59
|
+
description: 'Prefer `await` over promise chaining.',
|
|
60
|
+
recommended: 'unopinionated',
|
|
61
|
+
},
|
|
62
|
+
messages,
|
|
63
|
+
languages: [
|
|
64
|
+
'js/js',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default config;
|
|
@@ -74,6 +74,7 @@ function getReplacement(valueNode) {
|
|
|
74
74
|
|
|
75
75
|
const {value, raw} = valueNode;
|
|
76
76
|
|
|
77
|
+
// Intentionally `Number.isInteger`, not `Number.isSafeInteger`: large integers beyond the safe range are exactly what bigint literals are for.
|
|
77
78
|
if (!Number.isInteger(value)) {
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
@@ -171,6 +172,9 @@ const config = {
|
|
|
171
172
|
fixable: 'code',
|
|
172
173
|
hasSuggestions: true,
|
|
173
174
|
messages,
|
|
175
|
+
languages: [
|
|
176
|
+
'js/js',
|
|
177
|
+
],
|
|
174
178
|
},
|
|
175
179
|
};
|
|
176
180
|
|
|
@@ -155,7 +155,7 @@ const create = context => {
|
|
|
155
155
|
|
|
156
156
|
// `element.classList.add('className');`
|
|
157
157
|
// `element.classList.remove('className');`
|
|
158
|
-
if (
|
|
158
|
+
if (clauses.some(node => !isClassListMethodCall(node, ['add', 'remove']))) {
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
|
|
@@ -181,7 +181,7 @@ const create = context => {
|
|
|
181
181
|
let text = `${elementText}${isOptional ? '?' : ''}.classList.toggle(${classNameText}${conditionText ? `, ${conditionText}` : ''})`;
|
|
182
182
|
|
|
183
183
|
if (!isExpression) {
|
|
184
|
-
text
|
|
184
|
+
text += ';';
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
if (needsSemicolon(sourceCode.getTokenBefore(node), context, text)) {
|
|
@@ -249,6 +249,9 @@ const config = {
|
|
|
249
249
|
fixable: 'code',
|
|
250
250
|
hasSuggestions: true,
|
|
251
251
|
messages,
|
|
252
|
+
languages: [
|
|
253
|
+
'js/js',
|
|
254
|
+
],
|
|
252
255
|
},
|
|
253
256
|
};
|
|
254
257
|
|
package/rules/prefer-date-now.js
CHANGED
|
@@ -96,7 +96,6 @@ const fixDefaultExpression = (fixer, sourceCode, node) => {
|
|
|
96
96
|
const nodeText = sourceCode.getText(node);
|
|
97
97
|
const lineText = sourceCode.lines[line - 1];
|
|
98
98
|
const isOnlyNodeOnLine = lineText.trim() === nodeText;
|
|
99
|
-
const endsWithWhitespace = lineText[column] === ' ';
|
|
100
99
|
|
|
101
100
|
if (isOnlyNodeOnLine) {
|
|
102
101
|
return fixer.removeRange([
|
|
@@ -105,6 +104,7 @@ const fixDefaultExpression = (fixer, sourceCode, node) => {
|
|
|
105
104
|
]);
|
|
106
105
|
}
|
|
107
106
|
|
|
107
|
+
const endsWithWhitespace = lineText[column] === ' ';
|
|
108
108
|
if (endsWithWhitespace) {
|
|
109
109
|
const [start, end] = sourceCode.getRange(node);
|
|
110
110
|
return fixer.removeRange([start, end + 1]);
|
|
@@ -118,7 +118,7 @@ const create = context => {
|
|
|
118
118
|
const {sourceCode} = context;
|
|
119
119
|
const functionStack = [];
|
|
120
120
|
|
|
121
|
-
const
|
|
121
|
+
const getDefaultParameterProblem = (node, left, right, assignment) => {
|
|
122
122
|
const currentFunction = functionStack.at(-1);
|
|
123
123
|
|
|
124
124
|
if (!currentFunction || !isDefaultExpression(left, right)) {
|
|
@@ -186,13 +186,13 @@ const create = context => {
|
|
|
186
186
|
|
|
187
187
|
context.on('AssignmentExpression', node => {
|
|
188
188
|
if (node.parent.type === 'ExpressionStatement' && node.parent.expression === node) {
|
|
189
|
-
return
|
|
189
|
+
return getDefaultParameterProblem(node.parent, node.left, node.right, true);
|
|
190
190
|
}
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
context.on('VariableDeclarator', node => {
|
|
194
194
|
if (node.parent.type === 'VariableDeclaration' && node.parent.declarations[0] === node) {
|
|
195
|
-
return
|
|
195
|
+
return getDefaultParameterProblem(node.parent, node.id, node.init, false);
|
|
196
196
|
}
|
|
197
197
|
});
|
|
198
198
|
};
|
|
@@ -211,6 +211,9 @@ const config = {
|
|
|
211
211
|
[MESSAGE_ID]: 'Prefer default parameters over reassignment.',
|
|
212
212
|
[MESSAGE_ID_SUGGEST]: 'Replace reassignment with default parameter.',
|
|
213
213
|
},
|
|
214
|
+
languages: [
|
|
215
|
+
'js/js',
|
|
216
|
+
],
|
|
214
217
|
},
|
|
215
218
|
};
|
|
216
219
|
|