eslint 9.16.0 → 9.17.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/README.md CHANGED
@@ -300,7 +300,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
300
300
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
301
301
  <p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
302
302
  <p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
303
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340?v=4" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>
303
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340?v=4" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>
304
304
  <h3>Technology Sponsors</h3>
305
305
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
306
306
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -107,7 +107,7 @@ SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
107
107
  }
108
108
 
109
109
  messages.forEach(problem => {
110
- if (Object.hasOwn(problem, "fix")) {
110
+ if (Object.hasOwn(problem, "fix") && problem.fix) {
111
111
  fixes.push(problem);
112
112
  } else {
113
113
  remainingMessages.push(problem);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Rule to enforce default clauses in switch statements to be last
2
+ * @fileoverview Rule to enforce `default` clauses in switch statements to be last
3
3
  * @author Milos Djermanovic
4
4
  */
5
5
 
@@ -15,7 +15,7 @@ module.exports = {
15
15
  type: "suggestion",
16
16
 
17
17
  docs: {
18
- description: "Enforce default clauses in switch statements to be last",
18
+ description: "Enforce `default` clauses in switch statements to be last",
19
19
  recommended: false,
20
20
  url: "https://eslint.org/docs/latest/rules/default-case-last"
21
21
  },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview enforce "for" loop update clause moving the counter in the right direction.(for-direction)
2
+ * @fileoverview enforce `for` loop update clause moving the counter in the right direction.(for-direction)
3
3
  * @author Aladdin-ADD<hh_2013@foxmail.com>
4
4
  */
5
5
 
@@ -21,7 +21,7 @@ module.exports = {
21
21
  type: "problem",
22
22
 
23
23
  docs: {
24
- description: "Enforce \"for\" loop update clause moving the counter in the right direction",
24
+ description: "Enforce `for` loop update clause moving the counter in the right direction",
25
25
  recommended: true,
26
26
  url: "https://eslint.org/docs/latest/rules/for-direction"
27
27
  },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Disallow reassignment of function parameters.
2
+ * @fileoverview Disallow reassigning function parameters.
3
3
  * @author Nat Burns
4
4
  */
5
5
  "use strict";
@@ -16,7 +16,7 @@ module.exports = {
16
16
  type: "suggestion",
17
17
 
18
18
  docs: {
19
- description: "Disallow reassigning `function` parameters",
19
+ description: "Disallow reassigning function parameters",
20
20
  recommended: false,
21
21
  url: "https://eslint.org/docs/latest/rules/no-param-reassign"
22
22
  },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Rule to flag when using javascript: urls
2
+ * @fileoverview Rule to disallow `javascript:` URLs
3
3
  * @author Ilya Volodin
4
4
  */
5
5
  /* eslint no-script-url: 0 -- Code is checking to report such URLs */
@@ -18,7 +18,7 @@ module.exports = {
18
18
  type: "suggestion",
19
19
 
20
20
  docs: {
21
- description: "Disallow `javascript:` urls",
21
+ description: "Disallow `javascript:` URLs",
22
22
  recommended: false,
23
23
  url: "https://eslint.org/docs/latest/rules/no-script-url"
24
24
  },
@@ -33,7 +33,7 @@ module.exports = {
33
33
  create(context) {
34
34
 
35
35
  /**
36
- * Check whether a node's static value starts with "javascript:" or not.
36
+ * Check whether a node's static value starts with `javascript:` or not.
37
37
  * And report an error for unexpected script URL.
38
38
  * @param {ASTNode} node node to check
39
39
  * @returns {void}
@@ -50,6 +50,8 @@ module.exports = {
50
50
  url: "https://eslint.org/docs/latest/rules/no-unused-vars"
51
51
  },
52
52
 
53
+ hasSuggestions: true,
54
+
53
55
  schema: [
54
56
  {
55
57
  oneOf: [
@@ -98,7 +100,8 @@ module.exports = {
98
100
 
99
101
  messages: {
100
102
  unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.",
101
- usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}."
103
+ usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}.",
104
+ removeVar: "Remove unused variable '{{varName}}'."
102
105
  }
103
106
  },
104
107
 
@@ -172,6 +175,7 @@ module.exports = {
172
175
  return "catch-clause";
173
176
  case "Parameter":
174
177
  return "parameter";
178
+
175
179
  default:
176
180
  return "variable";
177
181
  }
@@ -832,6 +836,602 @@ module.exports = {
832
836
  return unusedVars;
833
837
  }
834
838
 
839
+ /**
840
+ * fixes unused variables
841
+ * @param {Object} fixer fixer object
842
+ * @param {Object} unusedVar unused variable to fix
843
+ * @returns {Object} fixer object
844
+ */
845
+ function handleFixes(fixer, unusedVar) {
846
+ const id = unusedVar.identifiers[0];
847
+ const parent = id.parent;
848
+ const parentType = parent.type;
849
+ const tokenBefore = sourceCode.getTokenBefore(id);
850
+ const tokenAfter = sourceCode.getTokenAfter(id);
851
+ const isFunction = astUtils.isFunction;
852
+ const isLoop = astUtils.isLoop;
853
+ const allWriteReferences = unusedVar.references.filter(ref => ref.isWrite());
854
+
855
+ /**
856
+ * get range from token before of a given node
857
+ * @param {ASTNode} node node of identifier
858
+ * @param {number} skips number of token to skip
859
+ * @returns {number} start range of token before the identifier
860
+ */
861
+ function getPreviousTokenStart(node, skips) {
862
+ return sourceCode.getTokenBefore(node, skips).range[0];
863
+ }
864
+
865
+ /**
866
+ * get range to token after of a given node
867
+ * @param {ASTNode} node node of identifier
868
+ * @param {number} skips number of token to skip
869
+ * @returns {number} end range of token after the identifier
870
+ */
871
+ function getNextTokenEnd(node, skips) {
872
+ return sourceCode.getTokenAfter(node, skips).range[1];
873
+ }
874
+
875
+ /**
876
+ * get the value of token before of a given node
877
+ * @param {ASTNode} node node of identifier
878
+ * @returns {string} value of token before the identifier
879
+ */
880
+ function getTokenBeforeValue(node) {
881
+ return sourceCode.getTokenBefore(node).value;
882
+ }
883
+
884
+ /**
885
+ * get the value of token after of a given node
886
+ * @param {ASTNode} node node of identifier
887
+ * @returns {string} value of token after the identifier
888
+ */
889
+ function getTokenAfterValue(node) {
890
+ return sourceCode.getTokenAfter(node).value;
891
+ }
892
+
893
+ /**
894
+ * Check if an array has a single element with null as other element.
895
+ * @param {ASTNode} node ArrayPattern node
896
+ * @returns {boolean} true if array has single element with other null elements
897
+ */
898
+ function hasSingleElement(node) {
899
+ return node.elements.filter(e => e !== null).length === 1;
900
+ }
901
+
902
+ /**
903
+ * check whether import specifier has an import of particular type
904
+ * @param {ASTNode} node ImportDeclaration node
905
+ * @param {string} type type of import to check
906
+ * @returns {boolean} true if import specifier has import of specified type
907
+ */
908
+ function hasImportOfCertainType(node, type) {
909
+ return node.specifiers.some(e => e.type === type);
910
+ }
911
+
912
+ /**
913
+ * Check whether declaration is safe to remove or not
914
+ * @param {ASTNode} nextToken next token of unused variable
915
+ * @param {ASTNode} prevToken previous token of unused variable
916
+ * @returns {boolean} true if declaration is not safe to remove
917
+ */
918
+ function isDeclarationNotSafeToRemove(nextToken, prevToken) {
919
+ return (
920
+ (nextToken.type === "String") ||
921
+ (
922
+ prevToken &&
923
+ !astUtils.isSemicolonToken(prevToken) &&
924
+ !astUtils.isOpeningBraceToken(prevToken)
925
+ )
926
+ );
927
+ }
928
+
929
+ /**
930
+ * give fixes for unused variables in function parameters
931
+ * @param {ASTNode} node node to check
932
+ * @returns {Object} fixer object
933
+ */
934
+ function fixFunctionParameters(node) {
935
+ const parentNode = node.parent;
936
+
937
+ if (isFunction(parentNode)) {
938
+
939
+ // remove unused function parameter if there is only a single parameter
940
+ if (parentNode.params.length === 1) {
941
+ return fixer.removeRange(node.range);
942
+ }
943
+
944
+ // remove first unused function parameter when there are multiple parameters
945
+ if (getTokenBeforeValue(node) === "(" && getTokenAfterValue(node) === ",") {
946
+ return fixer.removeRange([node.range[0], getNextTokenEnd(node)]);
947
+ }
948
+
949
+ // remove unused function parameters except first one when there are multiple parameters
950
+ return fixer.removeRange([getPreviousTokenStart(node), node.range[1]]);
951
+ }
952
+
953
+ return null;
954
+ }
955
+
956
+ /**
957
+ * fix unused variable declarations and function parameters
958
+ * @param {ASTNode} node parent node to identifier
959
+ * @returns {Object} fixer object
960
+ */
961
+ function fixVariables(node) {
962
+ const parentNode = node.parent;
963
+
964
+ // remove unused declared variables such as var a = b; or var a = b, c;
965
+ if (parentNode.type === "VariableDeclarator") {
966
+
967
+ // skip variable in for (const [ foo ] of bar);
968
+ if (isLoop(parentNode.parent.parent)) {
969
+ return null;
970
+ }
971
+
972
+ /*
973
+ * remove unused declared variable with single declaration such as 'var a = b;'
974
+ * remove complete declaration when there is an unused variable in 'const { a } = foo;', same for arrays.
975
+ */
976
+ if (parentNode.parent.declarations.length === 1) {
977
+
978
+ // if next token is a string it could become a directive if node is removed -> no suggestion.
979
+ const nextToken = sourceCode.getTokenAfter(parentNode.parent);
980
+
981
+ // if previous token exists and is not ";" or "{" not sure about ASI rules -> no suggestion.
982
+ const prevToken = sourceCode.getTokenBefore(parentNode.parent);
983
+
984
+ if (nextToken && isDeclarationNotSafeToRemove(nextToken, prevToken)) {
985
+ return null;
986
+ }
987
+
988
+ return fixer.removeRange(parentNode.parent.range);
989
+ }
990
+
991
+ /*
992
+ * remove unused declared variable with multiple declaration except first one such as 'var a = b, c = d;'
993
+ * fix 'let bar = "hello", { a } = foo;' to 'let bar = "hello";' if 'a' is unused, same for arrays.
994
+ */
995
+ if (getTokenBeforeValue(parentNode) === ",") {
996
+ return fixer.removeRange([getPreviousTokenStart(parentNode), parentNode.range[1]]);
997
+ }
998
+
999
+ /*
1000
+ * remove first unused declared variable when there are multiple declarations
1001
+ * fix 'let { a } = foo, bar = "hello";' to 'let bar = "hello";' if 'a' is unused, same for arrays.
1002
+ */
1003
+ return fixer.removeRange([parentNode.range[0], getNextTokenEnd(parentNode)]);
1004
+ }
1005
+
1006
+ // fixes [{a: {k}}], [{a: [k]}]
1007
+ if (getTokenBeforeValue(node) === ":") {
1008
+ if (parentNode.parent.type === "ObjectPattern") {
1009
+ // eslint-disable-next-line no-use-before-define -- due to interdependency of functions
1010
+ return fixObjectWithValueSeparator(node);
1011
+ }
1012
+ }
1013
+
1014
+ // fix unused function parameters
1015
+ return fixFunctionParameters(node);
1016
+ }
1017
+
1018
+ /**
1019
+ * fix nested object like { a: { b } }
1020
+ * @param {ASTNode} node parent node to check
1021
+ * @returns {Object} fixer object
1022
+ */
1023
+ function fixNestedObjectVariable(node) {
1024
+ const parentNode = node.parent;
1025
+
1026
+ // fix for { a: { b: { c: { d } } } }
1027
+ if (
1028
+ parentNode.parent.parent.parent.type === "ObjectPattern" &&
1029
+ parentNode.parent.properties.length === 1
1030
+ ) {
1031
+ return fixNestedObjectVariable(parentNode.parent);
1032
+ }
1033
+
1034
+ // fix for { a: { b } }
1035
+ if (parentNode.parent.type === "ObjectPattern") {
1036
+
1037
+ // fix for unused variables in dectructured object with single property in variable decalartion and function parameter
1038
+ if (parentNode.parent.properties.length === 1) {
1039
+ return fixVariables(parentNode.parent);
1040
+ }
1041
+
1042
+ // fix for first unused property when there are multiple properties such as '{ a: { b }, c }'
1043
+ if (getTokenBeforeValue(parentNode) === "{") {
1044
+ return fixer.removeRange(
1045
+ [parentNode.range[0], getNextTokenEnd(parentNode)]
1046
+ );
1047
+ }
1048
+
1049
+ // fix for unused property except first one when there are multiple properties such as '{ k, a: { b } }'
1050
+ return fixer.removeRange([getPreviousTokenStart(parentNode), parentNode.range[1]]);
1051
+ }
1052
+
1053
+ return null;
1054
+ }
1055
+
1056
+ /**
1057
+ * fix unused variables in array and nested array
1058
+ * @param {ASTNode} node parent node to check
1059
+ * @returns {Object} fixer object
1060
+ */
1061
+ function fixNestedArrayVariable(node) {
1062
+ const parentNode = node.parent;
1063
+
1064
+ // fix for nested arrays [[ a ]]
1065
+ if (parentNode.parent.type === "ArrayPattern" && hasSingleElement(parentNode)) {
1066
+ return fixNestedArrayVariable(parentNode);
1067
+ }
1068
+
1069
+ if (hasSingleElement(parentNode)) {
1070
+
1071
+ // fixes { a: [{ b }] } or { a: [[ b ]] }
1072
+ if (getTokenBeforeValue(parentNode) === ":") {
1073
+ return fixVariables(parentNode);
1074
+ }
1075
+
1076
+ // fixes [a, ...[[ b ]]] or [a, ...[{ b }]]
1077
+ if (parentNode.parent.type === "RestElement") {
1078
+ // eslint-disable-next-line no-use-before-define -- due to interdependency of functions
1079
+ return fixRestInPattern(parentNode.parent);
1080
+ }
1081
+
1082
+ // fix unused variables in destructured array in variable declaration or function parameter
1083
+ return fixVariables(parentNode);
1084
+ }
1085
+
1086
+ // remove last unused array element
1087
+ if (
1088
+ getTokenBeforeValue(node) === "," &&
1089
+ getTokenAfterValue(node) === "]"
1090
+ ) {
1091
+ return fixer.removeRange([getPreviousTokenStart(node), node.range[1]]);
1092
+ }
1093
+
1094
+ // remove unused array element
1095
+ return fixer.removeRange(node.range);
1096
+ }
1097
+
1098
+ /**
1099
+ * fix cases like {a: {k}} or {a: [k]}
1100
+ * @param {ASTNode} node parent node to check
1101
+ * @returns {Object} fixer object
1102
+ */
1103
+ function fixObjectWithValueSeparator(node) {
1104
+ const parentNode = node.parent.parent;
1105
+
1106
+ // fix cases like [{a : { b }}] or [{a : [ b ]}]
1107
+ if (
1108
+ parentNode.parent.type === "ArrayPattern" &&
1109
+ parentNode.properties.length === 1
1110
+ ) {
1111
+ return fixNestedArrayVariable(parentNode);
1112
+ }
1113
+
1114
+ // fix cases like {a: {k}} or {a: [k]}
1115
+ return fixNestedObjectVariable(node);
1116
+ }
1117
+
1118
+ /**
1119
+ * fix ...[[a]] or ...[{a}] like patterns
1120
+ * @param {ASTNode} node parent node to check
1121
+ * @returns {Object} fixer object
1122
+ */
1123
+ function fixRestInPattern(node) {
1124
+ const parentNode = node.parent;
1125
+
1126
+ // fix ...[[a]] or ...[{a}] in function parameters
1127
+ if (isFunction(parentNode)) {
1128
+ if (parentNode.params.length === 1) {
1129
+ return fixer.removeRange(node.range);
1130
+ }
1131
+
1132
+ return fixer.removeRange([getPreviousTokenStart(node), node.range[1]]);
1133
+ }
1134
+
1135
+ // fix rest in nested array pattern like [[a, ...[b]]]
1136
+ if (parentNode.type === "ArrayPattern") {
1137
+
1138
+ // fix [[...[b]]]
1139
+ if (hasSingleElement(parentNode)) {
1140
+ if (parentNode.parent.type === "ArrayPattern") {
1141
+ return fixNestedArrayVariable(parentNode);
1142
+ }
1143
+
1144
+ // fix 'const [...[b]] = foo; and function foo([...[b]]) {}
1145
+ return fixVariables(parentNode);
1146
+ }
1147
+
1148
+ // fix [[a, ...[b]]]
1149
+ return fixer.removeRange([getPreviousTokenStart(node), node.range[1]]);
1150
+ }
1151
+
1152
+ return null;
1153
+ }
1154
+
1155
+ // skip fix when variable has references that would be left behind
1156
+ if (allWriteReferences.some(ref => ref.identifier.range[0] !== id.range[0])) {
1157
+ return null;
1158
+ }
1159
+
1160
+ // remove declared variables such as var a; or var a, b;
1161
+ if (parentType === "VariableDeclarator") {
1162
+ if (parent.parent.declarations.length === 1) {
1163
+
1164
+ // prevent fix of variable in forOf and forIn loops.
1165
+ if (isLoop(parent.parent.parent) && parent.parent.parent.body !== parent.parent) {
1166
+ return null;
1167
+ }
1168
+
1169
+ // removes only variable not semicolon in 'if (foo()) var bar;' or in 'loops' or in 'with' statement.
1170
+ if (
1171
+ parent.parent.parent.type === "IfStatement" ||
1172
+ isLoop(parent.parent.parent) ||
1173
+ (parent.parent.parent.type === "WithStatement" && parent.parent.parent.body === parent.parent)
1174
+ ) {
1175
+ return fixer.replaceText(parent.parent, ";");
1176
+ }
1177
+
1178
+ // if next token is a string it could become a directive if node is removed -> no suggestion.
1179
+ const nextToken = sourceCode.getTokenAfter(parent.parent);
1180
+
1181
+ // if previous token exists and is not ";" or "{" not sure about ASI rules -> no suggestion.
1182
+ const prevToken = sourceCode.getTokenBefore(parent.parent);
1183
+
1184
+ if (nextToken && isDeclarationNotSafeToRemove(nextToken, prevToken)) {
1185
+ return null;
1186
+ }
1187
+
1188
+ // remove unused declared variable with single declaration like 'var a = b;'
1189
+ return fixer.removeRange(parent.parent.range);
1190
+ }
1191
+
1192
+ // remove unused declared variable with multiple declaration except first one like 'var a = b, c = d;'
1193
+ if (tokenBefore.value === ",") {
1194
+ return fixer.removeRange([tokenBefore.range[0], parent.range[1]]);
1195
+ }
1196
+
1197
+ // remove first unused declared variable when there are multiple declarations
1198
+ return fixer.removeRange([parent.range[0], getNextTokenEnd(parent)]);
1199
+ }
1200
+
1201
+ // remove variables in object patterns
1202
+ if (parent.parent.type === "ObjectPattern") {
1203
+ if (parent.parent.properties.length === 1) {
1204
+
1205
+ // fix [a, ...{b}]
1206
+ if (parent.parent.parent.type === "RestElement") {
1207
+ return fixRestInPattern(parent.parent.parent);
1208
+ }
1209
+
1210
+ // fix [{ a }]
1211
+ if (parent.parent.parent.type === "ArrayPattern") {
1212
+ return fixNestedArrayVariable(parent.parent);
1213
+ }
1214
+
1215
+ /*
1216
+ * var {a} = foo;
1217
+ * function a({a}) {}
1218
+ * fix const { a: { b } } = foo;
1219
+ */
1220
+ return fixVariables(parent.parent);
1221
+ }
1222
+
1223
+ // fix const { a:b } = foo;
1224
+ if (tokenBefore.value === ":") {
1225
+
1226
+ // remove first unused variable in const { a:b } = foo;
1227
+ if (getTokenBeforeValue(parent) === "{" && getTokenAfterValue(parent) === ",") {
1228
+ return fixer.removeRange([parent.range[0], getNextTokenEnd(parent)]);
1229
+ }
1230
+
1231
+ // remove unused variables in const { a: b, c: d } = foo; except first one
1232
+ return fixer.removeRange([getPreviousTokenStart(parent), id.range[1]]);
1233
+ }
1234
+ }
1235
+
1236
+ // remove unused variables inside an array
1237
+ if (parentType === "ArrayPattern") {
1238
+ if (hasSingleElement(parent)) {
1239
+
1240
+ // fix [a, ...[b]]
1241
+ if (parent.parent.type === "RestElement") {
1242
+ return fixRestInPattern(parent.parent);
1243
+ }
1244
+
1245
+ // fix [ [a] ]
1246
+ if (parent.parent.type === "ArrayPattern") {
1247
+ return fixNestedArrayVariable(parent);
1248
+ }
1249
+
1250
+ /*
1251
+ * fix var [a] = foo;
1252
+ * fix function foo([a]) {}
1253
+ * fix const { a: [b] } = foo;
1254
+ */
1255
+ return fixVariables(parent);
1256
+ }
1257
+
1258
+ // if "a" is unused in [a, b ,c] fixes to [, b, c]
1259
+ if (tokenBefore.value === "," && tokenAfter.value === ",") {
1260
+ return fixer.removeRange(id.range);
1261
+ }
1262
+ }
1263
+
1264
+ // remove unused rest elements
1265
+ if (parentType === "RestElement") {
1266
+
1267
+ // fix [a, ...rest]
1268
+ if (parent.parent.type === "ArrayPattern") {
1269
+ if (hasSingleElement(parent.parent)) {
1270
+
1271
+ // fix [[...rest]] when there is only rest element
1272
+ if (
1273
+ parent.parent.parent.type === "ArrayPattern"
1274
+ ) {
1275
+ return fixNestedArrayVariable(parent.parent);
1276
+ }
1277
+
1278
+ // fix 'const [...rest] = foo;' and 'function foo([...rest]) {}'
1279
+ return fixVariables(parent.parent);
1280
+ }
1281
+
1282
+ // fix [a, ...rest]
1283
+ return fixer.removeRange([getPreviousTokenStart(id, 1), id.range[1]]);
1284
+ }
1285
+
1286
+ // fix { a, ...rest}
1287
+ if (parent.parent.type === "ObjectPattern") {
1288
+
1289
+ // fix 'const {...rest} = foo;' and 'function foo({...rest}) {}'
1290
+ if (parent.parent.properties.length === 1) {
1291
+ return fixVariables(parent.parent);
1292
+ }
1293
+
1294
+ // fix { a, ...rest} when there are multiple properties
1295
+ return fixer.removeRange([getPreviousTokenStart(id, 1), id.range[1]]);
1296
+ }
1297
+
1298
+ // fix function foo(...rest) {}
1299
+ if (isFunction(parent.parent)) {
1300
+
1301
+ // remove unused rest in function parameter if there is only single parameter
1302
+ if (parent.parent.params.length === 1) {
1303
+ return fixer.removeRange(parent.range);
1304
+ }
1305
+
1306
+ // remove unused rest in function parameter if there multiple parameter
1307
+ return fixer.removeRange([getPreviousTokenStart(parent), parent.range[1]]);
1308
+ }
1309
+ }
1310
+
1311
+ if (parentType === "AssignmentPattern") {
1312
+
1313
+ // fix [a = aDefault]
1314
+ if (parent.parent.type === "ArrayPattern") {
1315
+ return fixNestedArrayVariable(parent);
1316
+ }
1317
+
1318
+ // fix {a = aDefault}
1319
+ if (parent.parent.parent.type === "ObjectPattern") {
1320
+ if (parent.parent.parent.properties.length === 1) {
1321
+
1322
+ // fixes [{a = aDefault}]
1323
+ if (parent.parent.parent.parent.type === "ArrayPattern") {
1324
+ return fixNestedArrayVariable(parent.parent.parent);
1325
+ }
1326
+
1327
+ // fix 'const {a = aDefault} = foo;' and 'function foo({a = aDefault}) {}'
1328
+ return fixVariables(parent.parent.parent);
1329
+ }
1330
+
1331
+ // fix unused 'a' in {a = aDefault} if it is the first property
1332
+ if (
1333
+ getTokenBeforeValue(parent.parent) === "{" &&
1334
+ getTokenAfterValue(parent.parent) === ","
1335
+ ) {
1336
+ return fixer.removeRange([parent.parent.range[0], getNextTokenEnd(parent.parent)]);
1337
+ }
1338
+
1339
+ // fix unused 'b' in {a, b = aDefault} if it is not the first property
1340
+ return fixer.removeRange([getPreviousTokenStart(parent.parent), parent.parent.range[1]]);
1341
+ }
1342
+
1343
+ // fix unused assignment patterns in function parameters
1344
+ if (isFunction(parent.parent)) {
1345
+ return fixFunctionParameters(parent);
1346
+ }
1347
+ }
1348
+
1349
+ // remove unused functions
1350
+ if (parentType === "FunctionDeclaration" && parent.id === id) {
1351
+ return fixer.removeRange(parent.range);
1352
+ }
1353
+
1354
+ // remove unused default import
1355
+ if (parentType === "ImportDefaultSpecifier") {
1356
+
1357
+ // remove unused default import when there are not other imports
1358
+ if (
1359
+ !hasImportOfCertainType(parent.parent, "ImportSpecifier") &&
1360
+ !hasImportOfCertainType(parent.parent, "ImportNamespaceSpecifier")
1361
+ ) {
1362
+ return fixer.removeRange([parent.range[0], parent.parent.source.range[0]]);
1363
+ }
1364
+
1365
+ // remove unused default import when there are other imports also
1366
+ return fixer.removeRange([id.range[0], tokenAfter.range[1]]);
1367
+ }
1368
+
1369
+ if (parentType === "ImportSpecifier") {
1370
+
1371
+ // remove unused imports when there is a single import
1372
+ if (parent.parent.specifiers.filter(e => e.type === "ImportSpecifier").length === 1) {
1373
+
1374
+ // remove unused import when there is no default import
1375
+ if (!hasImportOfCertainType(parent.parent, "ImportDefaultSpecifier")) {
1376
+ return fixer.removeRange(parent.parent.range);
1377
+ }
1378
+
1379
+ // fixes "import foo from 'module';" to "import 'module';"
1380
+ return fixer.removeRange([getPreviousTokenStart(parent, 1), tokenAfter.range[1]]);
1381
+ }
1382
+
1383
+ if (getTokenBeforeValue(parent) === "{") {
1384
+ return fixer.removeRange([parent.range[0], getNextTokenEnd(parent)]);
1385
+ }
1386
+
1387
+ return fixer.removeRange([getPreviousTokenStart(parent), parent.range[1]]);
1388
+ }
1389
+
1390
+ if (parentType === "ImportNamespaceSpecifier") {
1391
+ if (hasImportOfCertainType(parent.parent, "ImportDefaultSpecifier")) {
1392
+ return fixer.removeRange([getPreviousTokenStart(parent), parent.range[1]]);
1393
+ }
1394
+
1395
+ // fixes "import * as foo from 'module';" to "import 'module';"
1396
+ return fixer.removeRange([parent.range[0], parent.parent.source.range[0]]);
1397
+ }
1398
+
1399
+ // skip error in catch(error) variable
1400
+ if (parentType === "CatchClause") {
1401
+ return null;
1402
+ }
1403
+
1404
+ // remove unused declared classes
1405
+ if (parentType === "ClassDeclaration") {
1406
+ return fixer.removeRange(parent.range);
1407
+ }
1408
+
1409
+ // remove unused varible that is in a sequence [a,b] fixes to [a]
1410
+ if (tokenBefore?.value === ",") {
1411
+ return fixer.removeRange([tokenBefore.range[0], id.range[1]]);
1412
+ }
1413
+
1414
+ // remove unused varible that is in a sequence inside function arguments and object pattern
1415
+ if (tokenAfter.value === ",") {
1416
+
1417
+ // fix function foo(a, b) {}
1418
+ if (tokenBefore.value === "(") {
1419
+ return fixer.removeRange([id.range[0], tokenAfter.range[1]]);
1420
+ }
1421
+
1422
+ // fix const {a, b} = foo;
1423
+ if (tokenBefore.value === "{") {
1424
+ return fixer.removeRange([id.range[0], tokenAfter.range[1]]);
1425
+ }
1426
+ }
1427
+
1428
+ if (parentType === "ArrowFunctionExpression" && parent.params.length === 1 && tokenAfter?.value !== ")") {
1429
+ return fixer.replaceText(id, "()");
1430
+ }
1431
+
1432
+ return fixer.removeRange(id.range);
1433
+ }
1434
+
835
1435
  //--------------------------------------------------------------------------
836
1436
  // Public
837
1437
  //--------------------------------------------------------------------------
@@ -860,7 +1460,18 @@ module.exports = {
860
1460
  messageId: "unusedVar",
861
1461
  data: unusedVar.references.some(ref => ref.isWrite())
862
1462
  ? getAssignedMessageData(unusedVar)
863
- : getDefinedMessageData(unusedVar)
1463
+ : getDefinedMessageData(unusedVar),
1464
+ suggest: [
1465
+ {
1466
+ messageId: "removeVar",
1467
+ data: {
1468
+ varName: unusedVar.name
1469
+ },
1470
+ fix(fixer) {
1471
+ return handleFixes(fixer, unusedVar);
1472
+ }
1473
+ }
1474
+ ]
864
1475
  });
865
1476
 
866
1477
  // If there are no regular declaration, report the first `/*globals*/` comment directive.
@@ -309,6 +309,15 @@ module.exports = {
309
309
  }
310
310
 
311
311
 
312
+ if (targetAssignment.variable.references.some(ref => ref.identifier.type !== "Identifier")) {
313
+
314
+ /**
315
+ * Skip checking for a variable that has at least one non-identifier reference.
316
+ * It's generated by plugins and cannot be handled reliably in the core rule.
317
+ */
318
+ return;
319
+ }
320
+
312
321
  const readReferences = targetAssignment.variable.references.filter(reference => reference.isRead());
313
322
 
314
323
  if (!readReferences.length) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Prefers object spread property over Object.assign
2
+ * @fileoverview Rule to disallow using `Object.assign` with an object literal as the first argument and prefer the use of object spread instead
3
3
  * @author Sharmila Jesupaul
4
4
  */
5
5
 
@@ -246,7 +246,7 @@ module.exports = {
246
246
 
247
247
  docs: {
248
248
  description:
249
- "Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead",
249
+ "Disallow using `Object.assign` with an object literal as the first argument and prefer the use of object spread instead",
250
250
  recommended: false,
251
251
  url: "https://eslint.org/docs/latest/rules/prefer-object-spread"
252
252
  },
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Rule to enforce the use of `u` flag on RegExp.
2
+ * @fileoverview Rule to enforce the use of `u` or `v` flag on regular expressions.
3
3
  * @author Toru Nagashima
4
4
  */
5
5
 
@@ -48,7 +48,7 @@ module.exports = {
48
48
  type: "suggestion",
49
49
 
50
50
  docs: {
51
- description: "Enforce the use of `u` or `v` flag on RegExp",
51
+ description: "Enforce the use of `u` or `v` flag on regular expressions",
52
52
  recommended: false,
53
53
  url: "https://eslint.org/docs/latest/rules/require-unicode-regexp"
54
54
  },
@@ -10,7 +10,7 @@
10
10
  */
11
11
  const activeFlags = new Map([
12
12
  ["test_only", "Used only for testing."],
13
- ["unstable_config_lookup_from_file", "Look up eslint.config.js from the file being linted."],
13
+ ["unstable_config_lookup_from_file", "Look up `eslint.config.js` from the file being linted."],
14
14
  ["unstable_ts_config", "Enable TypeScript configuration files."]
15
15
  ]);
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.16.0",
3
+ "version": "9.17.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -102,7 +102,7 @@
102
102
  "@eslint/config-array": "^0.19.0",
103
103
  "@eslint/core": "^0.9.0",
104
104
  "@eslint/eslintrc": "^3.2.0",
105
- "@eslint/js": "9.16.0",
105
+ "@eslint/js": "9.17.0",
106
106
  "@eslint/plugin-kit": "^0.2.3",
107
107
  "@humanfs/node": "^0.16.6",
108
108
  "@humanwhocodes/module-importer": "^1.0.1",
@@ -111,7 +111,7 @@
111
111
  "@types/json-schema": "^7.0.15",
112
112
  "ajv": "^6.12.4",
113
113
  "chalk": "^4.0.0",
114
- "cross-spawn": "^7.0.5",
114
+ "cross-spawn": "^7.0.6",
115
115
  "debug": "^4.3.2",
116
116
  "escape-string-regexp": "^4.0.0",
117
117
  "eslint-scope": "^8.2.0",
@@ -154,7 +154,7 @@
154
154
  "eslint": "file:.",
155
155
  "eslint-config-eslint": "file:packages/eslint-config-eslint",
156
156
  "eslint-plugin-eslint-plugin": "^6.0.0",
157
- "eslint-plugin-expect-type": "^0.4.0",
157
+ "eslint-plugin-expect-type": "^0.6.0",
158
158
  "eslint-plugin-yml": "^1.14.0",
159
159
  "eslint-release": "^3.3.0",
160
160
  "eslint-rule-composer": "^0.3.0",