eslint-plugin-unicorn 64.0.0 → 65.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.
Files changed (100) hide show
  1. package/index.js +14 -0
  2. package/package.json +7 -3
  3. package/readme.md +183 -151
  4. package/rules/better-dom-traversing.js +291 -0
  5. package/rules/consistent-compound-words.js +452 -0
  6. package/rules/consistent-destructuring.js +97 -9
  7. package/rules/consistent-function-scoping.js +20 -0
  8. package/rules/consistent-json-file-read.js +245 -0
  9. package/rules/custom-error-definition.js +89 -4
  10. package/rules/dom-node-dataset.js +431 -0
  11. package/rules/escape-case.js +1 -1
  12. package/rules/expiring-todo-comments.js +42 -81
  13. package/rules/explicit-length-check.js +139 -6
  14. package/rules/filename-case.js +95 -21
  15. package/rules/import-style.js +21 -1
  16. package/rules/index.js +33 -3
  17. package/rules/isolated-functions.js +7 -5
  18. package/rules/no-anonymous-default-export.js +6 -6
  19. package/rules/no-array-callback-reference.js +89 -23
  20. package/rules/no-array-fill-with-reference-type.js +127 -0
  21. package/rules/no-array-for-each.js +17 -1
  22. package/rules/no-array-from-fill.js +76 -0
  23. package/rules/no-array-reduce.js +508 -2
  24. package/rules/no-blob-to-file.js +299 -0
  25. package/rules/no-canvas-to-image.js +152 -0
  26. package/rules/no-confusing-array-splice.js +164 -0
  27. package/rules/no-duplicate-set-values.js +57 -0
  28. package/rules/no-empty-file.js +31 -2
  29. package/rules/no-exports-in-scripts.js +34 -0
  30. package/rules/no-hex-escape.js +1 -1
  31. package/rules/no-incorrect-query-selector.js +347 -0
  32. package/rules/no-invalid-file-input-accept.js +475 -0
  33. package/rules/no-keyword-prefix.js +20 -5
  34. package/rules/no-late-current-target-access.js +166 -0
  35. package/rules/no-lonely-if.js +151 -50
  36. package/rules/no-manually-wrapped-comments.js +151 -0
  37. package/rules/no-null.js +14 -3
  38. package/rules/no-this-outside-of-class.js +75 -0
  39. package/rules/no-typeof-undefined.js +6 -4
  40. package/rules/no-unnecessary-nested-ternary.js +132 -0
  41. package/rules/no-unnecessary-polyfills.js +66 -20
  42. package/rules/no-unreadable-iife.js +33 -3
  43. package/rules/no-unused-array-method-return.js +301 -0
  44. package/rules/no-useless-promise-resolve-reject.js +5 -5
  45. package/rules/no-useless-spread.js +53 -4
  46. package/rules/no-useless-undefined.js +52 -45
  47. package/rules/numeric-separators-style.js +13 -4
  48. package/rules/prefer-array-last-methods.js +93 -0
  49. package/rules/prefer-array-some.js +66 -5
  50. package/rules/prefer-at.js +114 -55
  51. package/rules/prefer-dom-node-append.js +2 -2
  52. package/rules/prefer-dom-node-remove.js +24 -18
  53. package/rules/prefer-export-from.js +6 -6
  54. package/rules/prefer-get-or-insert-computed.js +159 -0
  55. package/rules/prefer-global-this.js +7 -46
  56. package/rules/prefer-https.js +83 -0
  57. package/rules/prefer-includes-over-repeated-comparisons.js +199 -0
  58. package/rules/prefer-iterator-concat.js +174 -0
  59. package/rules/prefer-iterator-to-array-at-end.js +123 -0
  60. package/rules/prefer-keyboard-event-key.js +202 -76
  61. package/rules/prefer-math-abs.js +397 -0
  62. package/rules/prefer-math-min-max.js +5 -3
  63. package/rules/prefer-node-protocol.js +6 -4
  64. package/rules/prefer-query-selector.js +119 -7
  65. package/rules/prefer-queue-microtask.js +204 -0
  66. package/rules/prefer-regexp-test.js +164 -18
  67. package/rules/prefer-set-has.js +411 -22
  68. package/rules/prefer-single-call.js +128 -38
  69. package/rules/prefer-split-limit.js +107 -0
  70. package/rules/prefer-spread.js +34 -4
  71. package/rules/prefer-string-match-all.js +352 -0
  72. package/rules/prefer-string-pad-start-end.js +380 -0
  73. package/rules/prefer-string-raw.js +90 -19
  74. package/rules/prefer-string-repeat.js +173 -0
  75. package/rules/prefer-string-replace-all.js +180 -12
  76. package/rules/prefer-string-slice.js +54 -3
  77. package/rules/prefer-top-level-await.js +129 -1
  78. package/rules/prevent-abbreviations.js +126 -134
  79. package/rules/require-css-escape.js +141 -0
  80. package/rules/require-module-specifiers.js +62 -0
  81. package/rules/require-passive-events.js +292 -0
  82. package/rules/shared/dom-events.js +7 -7
  83. package/rules/shared/identifier-checks.js +205 -0
  84. package/rules/shared/package-json.js +1 -1
  85. package/rules/string-content.js +34 -5
  86. package/rules/template-indent.js +52 -3
  87. package/rules/throw-new-error.js +4 -0
  88. package/rules/try-complexity.js +184 -0
  89. package/rules/utils/boolean.js +12 -12
  90. package/rules/utils/get-duplicate-array-elements.js +77 -0
  91. package/rules/utils/index.js +1 -0
  92. package/rules/utils/is-same-reference.js +6 -4
  93. package/rules/utils/should-add-parentheses-to-await-expression-argument.js +9 -9
  94. package/rules/utils/should-add-parentheses-to-call-expression-callee.js +12 -10
  95. package/rules/utils/should-add-parentheses-to-conditional-expression-child.js +9 -7
  96. package/rules/utils/should-add-parentheses-to-logical-expression-child.js +12 -8
  97. package/rules/utils/should-add-parentheses-to-unary-expression.js +10 -10
  98. package/rules/better-regex.js +0 -155
  99. package/rules/prefer-dom-node-dataset.js +0 -135
  100. package/rules/prefer-json-parse-buffer.js +0 -161
@@ -0,0 +1,452 @@
1
+ import helperValidatorIdentifier from '@babel/helper-validator-identifier';
2
+ import {
3
+ getAvailableVariableName,
4
+ getScopes,
5
+ isShorthandPropertyValue,
6
+ lowerFirst,
7
+ upperFirst,
8
+ } from './utils/index.js';
9
+ import {
10
+ isClassVariable,
11
+ shouldCheckDefaultOrNamespaceImportName,
12
+ shouldCheckShorthandImportName,
13
+ shouldRenameVariable,
14
+ shouldReportIdentifierAsProperty,
15
+ } from './shared/identifier-checks.js';
16
+ import {renameVariable} from './fix/index.js';
17
+
18
+ const MESSAGE_ID_ERROR = 'consistent-compound-words/error';
19
+ const MESSAGE_ID_RENAME = 'consistent-compound-words/rename';
20
+ const {isIdentifierName} = helperValidatorIdentifier;
21
+
22
+ const messages = {
23
+ [MESSAGE_ID_ERROR]: 'Prefer `{{replacement}}` over `{{name}}`.',
24
+ [MESSAGE_ID_RENAME]: 'Rename to `{{replacement}}`.',
25
+ };
26
+
27
+ const defaultReplacements = {
28
+ // Ambiguous/common API spellings like `fileName`, `setUp`, `lookUp`, `onLine`, `offLine`, `styleSheet`, and `superClass` are intentionally excluded.
29
+ backGround: 'background',
30
+ callBack: 'callback',
31
+ checkBox: 'checkbox',
32
+ clipBoard: 'clipboard',
33
+ codeBase: 'codebase',
34
+ dataBase: 'database',
35
+ downLoad: 'download',
36
+ feedBack: 'feedback',
37
+ foreGround: 'foreground',
38
+ frameWork: 'framework',
39
+ headLine: 'headline',
40
+ keyBoard: 'keyboard',
41
+ keyFrame: 'keyframe',
42
+ lifeCycle: 'lifecycle',
43
+ metaData: 'metadata',
44
+ midPoint: 'midpoint',
45
+ nameSpace: 'namespace',
46
+ newLine: 'newline',
47
+ overRide: 'override',
48
+ passWord: 'password',
49
+ payLoad: 'payload',
50
+ placeHolder: 'placeholder',
51
+ preView: 'preview',
52
+ screenShot: 'screenshot',
53
+ sideBar: 'sidebar',
54
+ subClass: 'subclass',
55
+ subDirectory: 'subdirectory',
56
+ subDomain: 'subdomain',
57
+ subMenu: 'submenu',
58
+ subProcess: 'subprocess',
59
+ subString: 'substring',
60
+ subTree: 'subtree',
61
+ subType: 'subtype',
62
+ subTitle: 'subtitle',
63
+ timeOut: 'timeout',
64
+ timeStamp: 'timestamp',
65
+ toolBar: 'toolbar',
66
+ toolKit: 'toolkit',
67
+ toolTip: 'tooltip',
68
+ touchScreen: 'touchscreen',
69
+ unSubscribe: 'unsubscribe',
70
+ underScore: 'underscore',
71
+ upLoad: 'upload',
72
+ userName: 'username',
73
+ viewPort: 'viewport',
74
+ webCam: 'webcam',
75
+ webHook: 'webhook',
76
+ webSite: 'website',
77
+ weekEnd: 'weekend',
78
+ whiteSpace: 'whitespace',
79
+ wildCard: 'wildcard',
80
+ workFlow: 'workflow',
81
+ workSpace: 'workspace',
82
+ };
83
+
84
+ const isUpperCase = string => string === string.toUpperCase();
85
+ const isUpperFirst = string => isUpperCase(string[0]);
86
+ const regExpSyntaxCharacters = new Set(String.raw`\^$.*+?()[]{}|`);
87
+ const escapeRegExp = string => {
88
+ let result = '';
89
+
90
+ for (const character of string) {
91
+ result += regExpSyntaxCharacters.has(character) ? `\\${character}` : character;
92
+ }
93
+
94
+ return result;
95
+ };
96
+
97
+ const prepareOptions = ({
98
+ checkProperties = false,
99
+ checkVariables = true,
100
+
101
+ checkDefaultAndNamespaceImports = 'internal',
102
+ checkShorthandImports = 'internal',
103
+ checkShorthandProperties = false,
104
+
105
+ extendDefaultReplacements = true,
106
+ replacements = {},
107
+
108
+ allowList = {},
109
+ } = {}) => {
110
+ const mergedReplacements = extendDefaultReplacements
111
+ ? {...defaultReplacements, ...replacements}
112
+ : replacements;
113
+
114
+ return {
115
+ checkProperties,
116
+ checkVariables,
117
+
118
+ checkDefaultAndNamespaceImports,
119
+ checkShorthandImports,
120
+ checkShorthandProperties,
121
+
122
+ replacements: new Map(Object.entries(mergedReplacements).filter(([, replacement]) => replacement !== false)),
123
+ allowList: new Set(Object.keys(allowList)),
124
+ };
125
+ };
126
+
127
+ const getReplacementForPart = (part, replacements) => {
128
+ const replacement = replacements.get(part) ?? replacements.get(lowerFirst(part));
129
+ if (!replacement) {
130
+ return;
131
+ }
132
+
133
+ return isUpperFirst(part) ? upperFirst(replacement) : lowerFirst(replacement);
134
+ };
135
+
136
+ const getNameReplacement = (name, {replacements, allowList}) => {
137
+ if (isUpperCase(name) || allowList.has(name)) {
138
+ return;
139
+ }
140
+
141
+ const boundary = String.raw`(?=$|[\d_$]|\p{Uppercase_Letter})`;
142
+ let replacement = name;
143
+ for (const discouragedName of replacements.keys()) {
144
+ const pattern = new RegExp(`^${escapeRegExp(discouragedName)}${boundary}|${escapeRegExp(upperFirst(discouragedName))}${boundary}`, 'gv');
145
+ replacement = replacement.replaceAll(pattern, part => getReplacementForPart(part, replacements));
146
+ }
147
+
148
+ if (replacement === name) {
149
+ return;
150
+ }
151
+
152
+ return replacement;
153
+ };
154
+
155
+ const createProblem = (node, replacement) => ({
156
+ node,
157
+ messageId: MESSAGE_ID_ERROR,
158
+ data: {
159
+ name: node.name,
160
+ replacement,
161
+ },
162
+ });
163
+
164
+ /** @param {import('eslint').Rule.RuleContext} context */
165
+ const create = context => {
166
+ const options = prepareOptions(context.options[0]);
167
+
168
+ const identifierToOuterClassVariable = new WeakMap();
169
+ const scopeToNamesGeneratedByFixer = new WeakMap();
170
+ const isSafeName = (name, scopes) => scopes.every(scope => {
171
+ const generatedNames = scopeToNamesGeneratedByFixer.get(scope);
172
+ return !generatedNames || !generatedNames.has(name);
173
+ });
174
+
175
+ const checkPossiblyWeirdClassVariable = variable => {
176
+ if (isClassVariable(variable)) {
177
+ if (variable.scope.type === 'class') {
178
+ const [definition] = variable.defs;
179
+ const outerClassVariable = identifierToOuterClassVariable.get(definition.name);
180
+
181
+ if (!outerClassVariable) {
182
+ return checkVariable(variable);
183
+ }
184
+
185
+ const combinedReferencesVariable = {
186
+ name: variable.name,
187
+ scope: variable.scope,
188
+ defs: variable.defs,
189
+ identifiers: variable.identifiers,
190
+ references: [...variable.references, ...outerClassVariable.references],
191
+ };
192
+
193
+ return checkVariable(combinedReferencesVariable);
194
+ }
195
+
196
+ const [definition] = variable.defs;
197
+ identifierToOuterClassVariable.set(definition.name, variable);
198
+
199
+ return;
200
+ }
201
+
202
+ return checkVariable(variable);
203
+ };
204
+
205
+ const checkVariable = variable => {
206
+ if (variable.defs.length === 0) {
207
+ return;
208
+ }
209
+
210
+ const [definition] = variable.defs;
211
+
212
+ if (!shouldCheckDefaultOrNamespaceImportName(definition, options)) {
213
+ return;
214
+ }
215
+
216
+ if (!shouldCheckShorthandImportName(definition, options, context)) {
217
+ return;
218
+ }
219
+
220
+ if (
221
+ !options.checkShorthandProperties
222
+ && isShorthandPropertyValue(definition.name)
223
+ ) {
224
+ return;
225
+ }
226
+
227
+ const replacement = getNameReplacement(variable.name, options);
228
+ if (!replacement) {
229
+ return;
230
+ }
231
+
232
+ const scopes = [
233
+ ...variable.references.map(reference => reference.from),
234
+ variable.scope,
235
+ ];
236
+ const safeReplacement = isIdentifierName(replacement)
237
+ ? getAvailableVariableName(replacement, scopes, isSafeName)
238
+ : undefined;
239
+ const problem = createProblem(definition.name, safeReplacement ?? replacement);
240
+
241
+ if (
242
+ safeReplacement
243
+ && shouldRenameVariable(variable)
244
+ && !variable.references.some(reference => reference.vueUsedInTemplate)
245
+ ) {
246
+ for (const scope of scopes) {
247
+ if (!scopeToNamesGeneratedByFixer.has(scope)) {
248
+ scopeToNamesGeneratedByFixer.set(scope, new Set());
249
+ }
250
+
251
+ const generatedNames = scopeToNamesGeneratedByFixer.get(scope);
252
+ generatedNames.add(safeReplacement);
253
+ }
254
+
255
+ problem.suggest = [
256
+ {
257
+ messageId: MESSAGE_ID_RENAME,
258
+ data: {replacement: safeReplacement},
259
+ fix: fixer => renameVariable(variable, safeReplacement, context, fixer),
260
+ },
261
+ ];
262
+ }
263
+
264
+ context.report(problem);
265
+ };
266
+
267
+ const checkVariables = scope => {
268
+ for (const variable of scope.variables) {
269
+ checkPossiblyWeirdClassVariable(variable);
270
+ }
271
+ };
272
+
273
+ const checkScope = scope => {
274
+ const scopes = getScopes(scope);
275
+ for (const scope of scopes) {
276
+ checkVariables(scope);
277
+ }
278
+ };
279
+
280
+ const isTSParameterPropertyName = node => {
281
+ if (options.checkVariables) {
282
+ return false;
283
+ }
284
+
285
+ if (node.parent.type === 'TSParameterProperty') {
286
+ return node.parent.parameter === node;
287
+ }
288
+
289
+ return (
290
+ node.parent.type === 'AssignmentPattern'
291
+ && node.parent.left === node
292
+ && node.parent.parent.type === 'TSParameterProperty'
293
+ && node.parent.parent.parameter === node.parent
294
+ );
295
+ };
296
+
297
+ const checkProperty = node => {
298
+ if (!options.checkProperties) {
299
+ return;
300
+ }
301
+
302
+ if (node.name === '__proto__') {
303
+ return;
304
+ }
305
+
306
+ const replacement = getNameReplacement(node.name, options);
307
+ if (!replacement) {
308
+ return;
309
+ }
310
+
311
+ if (node.parent.type === 'ExportSpecifier') {
312
+ return;
313
+ }
314
+
315
+ if (
316
+ !isTSParameterPropertyName(node)
317
+ && !shouldReportIdentifierAsProperty(node)
318
+ ) {
319
+ return;
320
+ }
321
+
322
+ const problem = createProblem(node, replacement);
323
+
324
+ context.report(problem);
325
+ };
326
+
327
+ context.on('Identifier', checkProperty);
328
+ context.on('PrivateIdentifier', checkProperty);
329
+ // eslint-disable-next-line no-warning-comments
330
+ // TODO: Consider expanding beyond JavaScript identifiers after this rule has proven itself.
331
+
332
+ context.on('Program:exit', program => {
333
+ if (!options.checkVariables) {
334
+ return;
335
+ }
336
+
337
+ checkScope(context.sourceCode.getScope(program));
338
+ });
339
+ };
340
+
341
+ const schema = {
342
+ type: 'array',
343
+ additionalItems: false,
344
+ items: [
345
+ {
346
+ type: 'object',
347
+ additionalProperties: false,
348
+ properties: {
349
+ checkProperties: {
350
+ type: 'boolean',
351
+ description: 'Whether to check property names.',
352
+ },
353
+ checkVariables: {
354
+ type: 'boolean',
355
+ description: 'Whether to check variable names.',
356
+ },
357
+ checkDefaultAndNamespaceImports: {
358
+ anyOf: [
359
+ {
360
+ type: 'boolean',
361
+ },
362
+ {
363
+ enum: [
364
+ 'internal',
365
+ ],
366
+ },
367
+ ],
368
+ description: 'Whether to check default and namespace import names.',
369
+ },
370
+ checkShorthandImports: {
371
+ anyOf: [
372
+ {
373
+ type: 'boolean',
374
+ },
375
+ {
376
+ enum: [
377
+ 'internal',
378
+ ],
379
+ },
380
+ ],
381
+ description: 'Whether to check shorthand import names.',
382
+ },
383
+ checkShorthandProperties: {
384
+ type: 'boolean',
385
+ description: 'Whether to check shorthand property names.',
386
+ },
387
+ extendDefaultReplacements: {
388
+ type: 'boolean',
389
+ description: 'Whether to extend the default replacements.',
390
+ },
391
+ replacements: {
392
+ $ref: '#/definitions/replacements',
393
+ description: 'Custom compound word replacements.',
394
+ },
395
+ allowList: {
396
+ $ref: '#/definitions/trueObject',
397
+ description: 'Custom allow list of names.',
398
+ },
399
+ },
400
+ },
401
+ ],
402
+ definitions: {
403
+ replacements: {
404
+ type: 'object',
405
+ propertyNames: {
406
+ minLength: 1,
407
+ },
408
+ additionalProperties: {
409
+ anyOf: [
410
+ {
411
+ enum: [
412
+ false,
413
+ ],
414
+ },
415
+ {
416
+ type: 'string',
417
+ minLength: 1,
418
+ },
419
+ ],
420
+ },
421
+ },
422
+ trueObject: {
423
+ type: 'object',
424
+ propertyNames: {
425
+ minLength: 1,
426
+ },
427
+ additionalProperties: {
428
+ enum: [
429
+ true,
430
+ ],
431
+ },
432
+ },
433
+ },
434
+ };
435
+
436
+ /** @type {import('eslint').Rule.RuleModule} */
437
+ const config = {
438
+ create,
439
+ meta: {
440
+ type: 'suggestion',
441
+ docs: {
442
+ description: 'Enforce consistent spelling of compound words in identifiers.',
443
+ recommended: 'unopinionated',
444
+ },
445
+ hasSuggestions: true,
446
+ schema,
447
+ defaultOptions: [{}],
448
+ messages,
449
+ },
450
+ };
451
+
452
+ export default config;
@@ -1,6 +1,6 @@
1
1
  import {findVariable} from '@eslint-community/eslint-utils';
2
2
  import {getAvailableVariableName, isLeftHandSide} from './utils/index.js';
3
- import {isCallOrNewExpression} from './ast/index.js';
3
+ import {isCallOrNewExpression, isStringLiteral} from './ast/index.js';
4
4
 
5
5
  const MESSAGE_ID = 'consistentDestructuring';
6
6
  const MESSAGE_ID_SUGGEST = 'consistentDestructuringSuggest';
@@ -111,6 +111,14 @@ const isMemberDestructuredInNestedPatternWithRest = (objectPattern, memberName)
111
111
  && property.value.type !== 'Identifier'
112
112
  && hasNestedRestElement(property.value));
113
113
 
114
+ const shouldIgnoreMemberExpression = node =>
115
+ node.computed
116
+ || (
117
+ isCallOrNewExpression(node.parent)
118
+ && node.parent.callee === node
119
+ )
120
+ || isLeftHandSide(node);
121
+
114
122
  const isRootVariableReassigned = (declaration, memberExpressionNode, memberScope, sourceCode) => {
115
123
  if (!declaration.rootVariable) {
116
124
  return false;
@@ -138,6 +146,88 @@ const isRootVariableReassigned = (declaration, memberExpressionNode, memberScope
138
146
  });
139
147
  };
140
148
 
149
+ const isMatchingInExpression = (node, memberExpression, sourceCode) => {
150
+ if (!(
151
+ node.type === 'BinaryExpression'
152
+ && node.operator === 'in'
153
+ && isStringLiteral(node.left)
154
+ && memberExpression.property.type === 'Identifier'
155
+ && node.left.value === memberExpression.property.name
156
+ )) {
157
+ return false;
158
+ }
159
+
160
+ return sourceCode.getText(node.right) === sourceCode.getText(memberExpression.object);
161
+ };
162
+
163
+ const hasMatchingInExpression = (node, memberExpression, sourceCode) => {
164
+ if (isMatchingInExpression(node, memberExpression, sourceCode)) {
165
+ return true;
166
+ }
167
+
168
+ if (
169
+ node.type !== 'LogicalExpression'
170
+ || node.operator !== '&&'
171
+ ) {
172
+ return false;
173
+ }
174
+
175
+ return hasMatchingInExpression(node.left, memberExpression, sourceCode)
176
+ || hasMatchingInExpression(node.right, memberExpression, sourceCode);
177
+ };
178
+
179
+ const isInPositiveGuardBranch = (parent, child, memberExpression, sourceCode) => {
180
+ switch (parent.type) {
181
+ case 'ConditionalExpression': {
182
+ return parent.consequent === child
183
+ && hasMatchingInExpression(parent.test, memberExpression, sourceCode);
184
+ }
185
+
186
+ case 'LogicalExpression': {
187
+ return parent.operator === '&&'
188
+ && parent.right === child
189
+ && hasMatchingInExpression(parent.left, memberExpression, sourceCode);
190
+ }
191
+
192
+ case 'IfStatement': {
193
+ return parent.consequent === child
194
+ && hasMatchingInExpression(parent.test, memberExpression, sourceCode);
195
+ }
196
+
197
+ default: {
198
+ return false;
199
+ }
200
+ }
201
+ };
202
+
203
+ const isInTypeGuardBoundary = node =>
204
+ [
205
+ 'AccessorProperty',
206
+ 'FunctionDeclaration',
207
+ 'MethodDefinition',
208
+ 'PropertyDefinition',
209
+ ].includes(node.type);
210
+
211
+ const isInTypeGuardedBranch = (node, sourceCode) => {
212
+ let child = node;
213
+ let {parent} = node;
214
+
215
+ while (parent) {
216
+ if (isInTypeGuardBoundary(parent)) {
217
+ return false;
218
+ }
219
+
220
+ if (isInPositiveGuardBranch(parent, child, node, sourceCode)) {
221
+ return true;
222
+ }
223
+
224
+ child = parent;
225
+ parent = parent.parent;
226
+ }
227
+
228
+ return false;
229
+ };
230
+
141
231
  /** @param {import('eslint').Rule.RuleContext} context */
142
232
  const create = context => {
143
233
  const {sourceCode} = context;
@@ -165,14 +255,7 @@ const create = context => {
165
255
  });
166
256
 
167
257
  context.on('MemberExpression', node => {
168
- if (
169
- node.computed
170
- || (
171
- isCallOrNewExpression(node.parent)
172
- && node.parent.callee === node
173
- )
174
- || isLeftHandSide(node)
175
- ) {
258
+ if (shouldIgnoreMemberExpression(node)) {
176
259
  return;
177
260
  }
178
261
 
@@ -205,6 +288,11 @@ const create = context => {
205
288
  }
206
289
 
207
290
  const member = sourceCode.getText(node.property);
291
+
292
+ if (isInTypeGuardedBranch(node, sourceCode)) {
293
+ return;
294
+ }
295
+
208
296
  const memberDestructuredInNestedPattern = isMemberDestructuredInNestedPatternWithRest(objectPattern, member);
209
297
 
210
298
  const destructuredProperties = objectPattern.properties.filter(property =>
@@ -99,6 +99,25 @@ const isArrowFunctionNodeWithThis = (node, visitorKeys) =>
99
99
  // Include both params and body, because parameter defaults can reference lexical `this`.
100
100
  && isNodeContainsLexicalThis(node, visitorKeys);
101
101
 
102
+ function isInsideJestMockFactory(node) {
103
+ let current = node;
104
+ while (current.parent) {
105
+ const {parent} = current;
106
+ if (
107
+ parent.type === 'CallExpression'
108
+ && parent.arguments[1] === current
109
+ && functionTypes.includes(current.type)
110
+ && isNodeMatches(parent.callee, ['jest.mock'])
111
+ ) {
112
+ return true;
113
+ }
114
+
115
+ current = parent;
116
+ }
117
+
118
+ return false;
119
+ }
120
+
102
121
  const iifeFunctionTypes = new Set([
103
122
  'FunctionExpression',
104
123
  'ArrowFunctionExpression',
@@ -151,6 +170,7 @@ function checkNode(node, scopeManager, sourceCode) {
151
170
  if (
152
171
  !scope
153
172
  || isArrowFunctionNodeWithThis(node, sourceCode.visitorKeys)
173
+ || isInsideJestMockFactory(node)
154
174
  ) {
155
175
  return true;
156
176
  }