@agilebot/eslint-plugin 0.3.5 → 0.3.6
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/LICENSE.tpl +8 -0
- package/README.md +2 -4
- package/dist/index.js +739 -996
- package/package.json +5 -3
package/dist/index.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license @agilebot/eslint-plugin v0.3.
|
2
|
+
* @license @agilebot/eslint-plugin v0.3.6
|
3
3
|
*
|
4
4
|
* Copyright (c) Agilebot, Inc. and its affiliates.
|
5
5
|
*
|
@@ -33,7 +33,7 @@ function _interopNamespaceDefault(e) {
|
|
33
33
|
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
34
34
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
35
35
|
|
36
|
-
var
|
36
|
+
var enforceMuiIconAlias = {
|
37
37
|
meta: {
|
38
38
|
type: 'problem',
|
39
39
|
docs: {
|
@@ -49,10 +49,7 @@ var enforceIconAlias = {
|
|
49
49
|
create(context) {
|
50
50
|
return {
|
51
51
|
ImportDeclaration(node) {
|
52
|
-
if (
|
53
|
-
node.source.value !== '@mui/icons-material' &&
|
54
|
-
node.source.value !== 'mdi-material-ui'
|
55
|
-
) {
|
52
|
+
if (node.source.value !== '@mui/icons-material' && node.source.value !== 'mdi-material-ui') {
|
56
53
|
return;
|
57
54
|
}
|
58
55
|
for (const specifier of node.specifiers) {
|
@@ -66,11 +63,7 @@ var enforceIconAlias = {
|
|
66
63
|
data: {
|
67
64
|
name: node.source.value
|
68
65
|
},
|
69
|
-
fix: fixer =>
|
70
|
-
fixer.replaceText(
|
71
|
-
specifier,
|
72
|
-
`${specifier.imported.name} as ${specifier.imported.name}Icon`
|
73
|
-
)
|
66
|
+
fix: fixer => fixer.replaceText(specifier, "".concat(specifier.imported.name, " as ").concat(specifier.imported.name, "Icon"))
|
74
67
|
});
|
75
68
|
}
|
76
69
|
}
|
@@ -80,11 +73,11 @@ var enforceIconAlias = {
|
|
80
73
|
};
|
81
74
|
|
82
75
|
function getSetting(context, name) {
|
83
|
-
return context.settings[
|
76
|
+
return context.settings["agilebot/".concat(name)];
|
84
77
|
}
|
85
78
|
|
86
79
|
let warnedForMissingPrefix = false;
|
87
|
-
var
|
80
|
+
var importMonorepo = {
|
88
81
|
meta: {
|
89
82
|
type: 'problem',
|
90
83
|
docs: {
|
@@ -108,7 +101,7 @@ var monorepo = {
|
|
108
101
|
}
|
109
102
|
return;
|
110
103
|
}
|
111
|
-
prefix =
|
104
|
+
prefix = "".concat(prefix, "/");
|
112
105
|
if (typeof node.source.value !== 'string') {
|
113
106
|
return;
|
114
107
|
}
|
@@ -124,10 +117,8 @@ var monorepo = {
|
|
124
117
|
module: node.source.value
|
125
118
|
},
|
126
119
|
fix: fixer => {
|
127
|
-
const correctedPath = values
|
128
|
-
|
129
|
-
.join('/');
|
130
|
-
return fixer.replaceText(node.source, `'${correctedPath}'`);
|
120
|
+
const correctedPath = values.filter((_, index) => index !== 2).join('/');
|
121
|
+
return fixer.replaceText(node.source, "'".concat(correctedPath, "'"));
|
131
122
|
}
|
132
123
|
});
|
133
124
|
}
|
@@ -137,74 +128,33 @@ var monorepo = {
|
|
137
128
|
};
|
138
129
|
|
139
130
|
function findFormatMessageAttrNode(node, attrName) {
|
140
|
-
if (
|
141
|
-
node.
|
142
|
-
(node.callee.name === 'formatMessage' || node.callee.name === '$t') &&
|
143
|
-
node.arguments.length > 0 &&
|
144
|
-
node.arguments[0].properties
|
145
|
-
) {
|
146
|
-
return node.arguments[0].properties.find(
|
147
|
-
a => a.key && a.key.name === attrName
|
148
|
-
);
|
131
|
+
if (node.type === 'CallExpression' && (node.callee.name === 'formatMessage' || node.callee.name === '$t') && node.arguments.length > 0 && node.arguments[0].properties) {
|
132
|
+
return node.arguments[0].properties.find(a => a.key && a.key.name === attrName);
|
149
133
|
}
|
150
|
-
if (
|
151
|
-
node.
|
152
|
-
node.callee.type === 'MemberExpression' &&
|
153
|
-
(node.callee.object.name === 'intl' ||
|
154
|
-
(node.callee.object.name && node.callee.object.name.endsWith('Intl'))) &&
|
155
|
-
(node.callee.property.name === 'formatMessage' ||
|
156
|
-
node.callee.property.name === '$t')
|
157
|
-
) {
|
158
|
-
return node.arguments[0].properties.find(
|
159
|
-
a => a.key && a.key.name === attrName
|
160
|
-
);
|
134
|
+
if (node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && (node.callee.object.name === 'intl' || node.callee.object.name && node.callee.object.name.endsWith('Intl')) && (node.callee.property.name === 'formatMessage' || node.callee.property.name === '$t')) {
|
135
|
+
return node.arguments[0].properties.find(a => a.key && a.key.name === attrName);
|
161
136
|
}
|
162
137
|
}
|
163
138
|
function findFormattedMessageAttrNode(node, attrName) {
|
164
|
-
if (
|
165
|
-
node.type === 'JSXIdentifier' &&
|
166
|
-
node.name === 'FormattedMessage' &&
|
167
|
-
node.parent &&
|
168
|
-
node.parent.type === 'JSXOpeningElement'
|
169
|
-
) {
|
139
|
+
if (node.type === 'JSXIdentifier' && node.name === 'FormattedMessage' && node.parent && node.parent.type === 'JSXOpeningElement') {
|
170
140
|
return node.parent.attributes.find(a => a.name && a.name.name === attrName);
|
171
141
|
}
|
172
142
|
}
|
173
143
|
function findAttrNodeInDefineMessages(node, attrName) {
|
174
|
-
if (
|
175
|
-
node.type === 'Property' &&
|
176
|
-
node.key.name === attrName &&
|
177
|
-
node.parent &&
|
178
|
-
node.parent.parent &&
|
179
|
-
node.parent.parent.parent &&
|
180
|
-
node.parent.parent.parent.parent &&
|
181
|
-
node.parent.parent.parent.parent.type === 'CallExpression' &&
|
182
|
-
node.parent.parent.parent.parent.callee.name === 'defineMessages'
|
183
|
-
) {
|
144
|
+
if (node.type === 'Property' && node.key.name === attrName && node.parent && node.parent.parent && node.parent.parent.parent && node.parent.parent.parent.parent && node.parent.parent.parent.parent.type === 'CallExpression' && node.parent.parent.parent.parent.callee.name === 'defineMessages') {
|
184
145
|
return node;
|
185
146
|
}
|
186
147
|
}
|
187
148
|
function findAttrNodeInDefineMessage(node, attrName) {
|
188
|
-
if (
|
189
|
-
node.type === 'Property' &&
|
190
|
-
node.key.name === attrName &&
|
191
|
-
node.parent &&
|
192
|
-
node.parent.parent &&
|
193
|
-
node.parent.parent.type === 'CallExpression' &&
|
194
|
-
node.parent.parent.callee.name === 'defineMessage'
|
195
|
-
) {
|
149
|
+
if (node.type === 'Property' && node.key.name === attrName && node.parent && node.parent.parent && node.parent.parent.type === 'CallExpression' && node.parent.parent.callee.name === 'defineMessage') {
|
196
150
|
return node;
|
197
151
|
}
|
198
152
|
}
|
199
153
|
function sortedTemplateElements(node) {
|
200
|
-
return [...node.quasis, ...node.expressions].sort(
|
201
|
-
(a, b) => a.range[0] - b.range[0]
|
202
|
-
);
|
154
|
+
return [...node.quasis, ...node.expressions].sort((a, b) => a.range[0] - b.range[0]);
|
203
155
|
}
|
204
156
|
function templateLiteralDisplayStr(node) {
|
205
|
-
return sortedTemplateElements(node)
|
206
|
-
.map(e => (!e.value ? '*' : e.value.raw))
|
207
|
-
.join('');
|
157
|
+
return sortedTemplateElements(node).map(e => !e.value ? '*' : e.value.raw).join('');
|
208
158
|
}
|
209
159
|
|
210
160
|
const localeFilesKeys = {};
|
@@ -218,22 +168,19 @@ function getIntlIds(context) {
|
|
218
168
|
localeFiles.forEach(f => {
|
219
169
|
const fullPath = projectRoot ? path__namespace.join(projectRoot, f) : f;
|
220
170
|
const mtime = fs__namespace.lstatSync(fullPath).mtime.getTime();
|
221
|
-
if (
|
222
|
-
!localeFilesKeys[fullPath] ||
|
223
|
-
mtime !== localeFilesKeys[fullPath].mtime
|
224
|
-
) {
|
171
|
+
if (!localeFilesKeys[fullPath] || mtime !== localeFilesKeys[fullPath].mtime) {
|
225
172
|
let json;
|
226
173
|
if (fullPath.endsWith('.json')) {
|
227
174
|
json = JSON.parse(fs__namespace.readFileSync(fullPath, 'utf8'));
|
228
175
|
} else if (fullPath.endsWith('.ts')) {
|
229
176
|
json = eslintUtils.tsImport(fullPath);
|
230
|
-
if (typeof json === 'object' && json
|
231
|
-
json = json
|
177
|
+
if (typeof json === 'object' && json["default"]) {
|
178
|
+
json = json["default"];
|
232
179
|
}
|
233
180
|
} else if (fullPath.endsWith('.js')) {
|
234
181
|
json = require(fullPath);
|
235
|
-
if (typeof json === 'object' && json
|
236
|
-
json = json
|
182
|
+
if (typeof json === 'object' && json["default"]) {
|
183
|
+
json = json["default"];
|
237
184
|
}
|
238
185
|
} else {
|
239
186
|
throw new Error('unsupported file extension');
|
@@ -248,7 +195,7 @@ function getIntlIds(context) {
|
|
248
195
|
return results;
|
249
196
|
}
|
250
197
|
|
251
|
-
var
|
198
|
+
var intlIdMissing = {
|
252
199
|
meta: {
|
253
200
|
docs: {
|
254
201
|
description: 'Validates intl message ids are in locale file',
|
@@ -275,9 +222,7 @@ var idMissing = {
|
|
275
222
|
}
|
276
223
|
}
|
277
224
|
function processTemplateLiteral(node) {
|
278
|
-
const exStr = sortedTemplateElements(node)
|
279
|
-
.map(e => (!e.value ? '.*' : e.value.raw))
|
280
|
-
.join('');
|
225
|
+
const exStr = sortedTemplateElements(node).map(e => !e.value ? '.*' : e.value.raw).join('');
|
281
226
|
const re = new RegExp(exStr);
|
282
227
|
if (!isTemplateTranslated(re)) {
|
283
228
|
context.report({
|
@@ -290,10 +235,7 @@ var idMissing = {
|
|
290
235
|
if (node.value.type === 'Literal') {
|
291
236
|
return processLiteral(node.value);
|
292
237
|
}
|
293
|
-
if (
|
294
|
-
node.value.type === 'JSXExpressionContainer' &&
|
295
|
-
node.value.expression.type === 'TemplateLiteral'
|
296
|
-
) {
|
238
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
297
239
|
return processTemplateLiteral(node.value.expression);
|
298
240
|
}
|
299
241
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -318,9 +260,7 @@ var idMissing = {
|
|
318
260
|
}
|
319
261
|
},
|
320
262
|
Property: function (node) {
|
321
|
-
const attrNode =
|
322
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
323
|
-
findAttrNodeInDefineMessage(node, 'id');
|
263
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
324
264
|
if (attrNode) {
|
325
265
|
return processAttrNode(attrNode);
|
326
266
|
}
|
@@ -329,33 +269,30 @@ var idMissing = {
|
|
329
269
|
}
|
330
270
|
};
|
331
271
|
|
332
|
-
var
|
272
|
+
var intlIdPrefix = {
|
333
273
|
meta: {
|
334
274
|
docs: {
|
335
275
|
description: 'Validates intl message ids has correct prefixes',
|
336
276
|
category: 'Intl'
|
337
277
|
},
|
338
278
|
fixable: undefined,
|
339
|
-
schema: [
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
type: 'string'
|
344
|
-
}
|
279
|
+
schema: [{
|
280
|
+
type: 'array',
|
281
|
+
items: {
|
282
|
+
type: 'string'
|
345
283
|
}
|
346
|
-
]
|
284
|
+
}]
|
347
285
|
},
|
348
286
|
create: function (context) {
|
349
287
|
if (context.options[0].length === 0) {
|
350
288
|
throw new Error('Prefixes are required in settings');
|
351
289
|
}
|
352
|
-
const hasPrefix = value =>
|
353
|
-
context.options[0].some(p => value.startsWith(p));
|
290
|
+
const hasPrefix = value => context.options[0].some(p => value.startsWith(p));
|
354
291
|
function report(node, value) {
|
355
292
|
if (!hasPrefix(value)) {
|
356
293
|
context.report({
|
357
294
|
node: node,
|
358
|
-
message:
|
295
|
+
message: "Invalid id prefix: ".concat(value)
|
359
296
|
});
|
360
297
|
}
|
361
298
|
}
|
@@ -370,10 +307,7 @@ var idPrefix = {
|
|
370
307
|
if (node.value.type === 'Literal') {
|
371
308
|
return processLiteral(node.value);
|
372
309
|
}
|
373
|
-
if (
|
374
|
-
node.value.type === 'JSXExpressionContainer' &&
|
375
|
-
node.value.expression.type === 'TemplateLiteral'
|
376
|
-
) {
|
310
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
377
311
|
return processTemplateLiteral(node.value.expression);
|
378
312
|
}
|
379
313
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -394,9 +328,7 @@ var idPrefix = {
|
|
394
328
|
}
|
395
329
|
},
|
396
330
|
Property: function (node) {
|
397
|
-
const attrNode =
|
398
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
399
|
-
findAttrNodeInDefineMessage(node, 'id');
|
331
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
400
332
|
if (attrNode) {
|
401
333
|
return processAttrNode(attrNode);
|
402
334
|
}
|
@@ -406,7 +338,7 @@ var idPrefix = {
|
|
406
338
|
};
|
407
339
|
|
408
340
|
const usedIds = new Map();
|
409
|
-
var
|
341
|
+
var intlIdUnused = {
|
410
342
|
meta: {
|
411
343
|
docs: {
|
412
344
|
description: 'Finds unused intl message ids in locale file',
|
@@ -438,9 +370,7 @@ var idUnused = {
|
|
438
370
|
}
|
439
371
|
}
|
440
372
|
function processTemplateLiteral(node) {
|
441
|
-
const exStr = sortedTemplateElements(node)
|
442
|
-
.map(e => (!e.value ? '.*' : e.value.raw))
|
443
|
-
.join('');
|
373
|
+
const exStr = sortedTemplateElements(node).map(e => !e.value ? '.*' : e.value.raw).join('');
|
444
374
|
const re = new RegExp(exStr);
|
445
375
|
if (isTemplateTranslated(re)) ;
|
446
376
|
}
|
@@ -448,10 +378,7 @@ var idUnused = {
|
|
448
378
|
if (node.value.type === 'Literal') {
|
449
379
|
return processLiteral(node.value);
|
450
380
|
}
|
451
|
-
if (
|
452
|
-
node.value.type === 'JSXExpressionContainer' &&
|
453
|
-
node.value.expression.type === 'TemplateLiteral'
|
454
|
-
) {
|
381
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
455
382
|
return processTemplateLiteral(node.value.expression);
|
456
383
|
}
|
457
384
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -472,9 +399,7 @@ var idUnused = {
|
|
472
399
|
}
|
473
400
|
},
|
474
401
|
Property: function (node) {
|
475
|
-
const attrNode =
|
476
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
477
|
-
findAttrNodeInDefineMessage(node, 'id');
|
402
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
478
403
|
if (attrNode) {
|
479
404
|
return processAttrNode(attrNode);
|
480
405
|
}
|
@@ -488,7 +413,7 @@ var idUnused = {
|
|
488
413
|
}
|
489
414
|
};
|
490
415
|
|
491
|
-
var
|
416
|
+
var intlNoDefault = {
|
492
417
|
meta: {
|
493
418
|
docs: {
|
494
419
|
description: 'Validates defaultMessage is not used with react-intl',
|
@@ -518,9 +443,7 @@ var noDefault = {
|
|
518
443
|
}
|
519
444
|
},
|
520
445
|
Property: function (node) {
|
521
|
-
const attrNode =
|
522
|
-
findAttrNodeInDefineMessages(node, 'defaultMessage') ||
|
523
|
-
findAttrNodeInDefineMessage(node, 'defaultMessage');
|
446
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'defaultMessage') || findAttrNodeInDefineMessage(node, 'defaultMessage');
|
524
447
|
if (attrNode) {
|
525
448
|
return processAttrNode(attrNode);
|
526
449
|
}
|
@@ -529,79 +452,213 @@ var noDefault = {
|
|
529
452
|
}
|
530
453
|
};
|
531
454
|
|
532
|
-
var
|
455
|
+
var noAsyncArrayMethods = {
|
456
|
+
meta: {
|
457
|
+
docs: {
|
458
|
+
description: 'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
|
459
|
+
category: 'Array',
|
460
|
+
recommended: true
|
461
|
+
},
|
462
|
+
fixable: undefined,
|
463
|
+
schema: [],
|
464
|
+
messages: {
|
465
|
+
noAsyncArrayMethods: "No async function in method '{{ methodName }}'"
|
466
|
+
}
|
467
|
+
},
|
468
|
+
create: function (context) {
|
469
|
+
return {
|
470
|
+
ExpressionStatement: function (node) {
|
471
|
+
const notAllowedArrayMethods = ['forEach', 'filter', 'some', 'every', 'map', 'reduce', 'reduceRight', 'flatMap', 'find', 'findIndex', 'findLast', 'findLastIndex'];
|
472
|
+
const {
|
473
|
+
callee
|
474
|
+
} = node.expression;
|
475
|
+
if (!callee || !callee.property || !callee.property.name) {
|
476
|
+
return;
|
477
|
+
}
|
478
|
+
if (notAllowedArrayMethods.includes(callee.property.name)) {
|
479
|
+
const functionArguments = node.expression.arguments.find(n => {
|
480
|
+
return ['ArrowFunctionExpression', 'FunctionExpression'].includes(n.type);
|
481
|
+
});
|
482
|
+
if (functionArguments && functionArguments.async) {
|
483
|
+
context.report({
|
484
|
+
node,
|
485
|
+
messageId: 'noAsyncArrayMethods',
|
486
|
+
data: {
|
487
|
+
methodName: callee.property.name
|
488
|
+
}
|
489
|
+
});
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
};
|
494
|
+
}
|
495
|
+
};
|
496
|
+
|
497
|
+
var noImportCss = {
|
498
|
+
meta: {
|
499
|
+
type: 'problem',
|
500
|
+
docs: {
|
501
|
+
description: 'Prevent importing CSS',
|
502
|
+
recommended: true
|
503
|
+
},
|
504
|
+
fixable: 'code',
|
505
|
+
schema: [],
|
506
|
+
messages: {
|
507
|
+
noImportCSS: 'Do not import CSS files. Use CSS-in-JS instead.'
|
508
|
+
}
|
509
|
+
},
|
510
|
+
create(context) {
|
511
|
+
return {
|
512
|
+
ImportDeclaration(node) {
|
513
|
+
const ext = path.extname(node.source.value);
|
514
|
+
if (ext.startsWith('.css') || ext.startsWith('.scss') || ext.startsWith('.sass') || ext.startsWith('.less') || ext.startsWith('.styl')) {
|
515
|
+
context.report({
|
516
|
+
node,
|
517
|
+
messageId: 'noImportCSS',
|
518
|
+
data: {
|
519
|
+
path: node.source.value
|
520
|
+
}
|
521
|
+
});
|
522
|
+
}
|
523
|
+
}
|
524
|
+
};
|
525
|
+
}
|
526
|
+
};
|
527
|
+
|
528
|
+
var noThenCatchFinally = {
|
529
|
+
meta: {
|
530
|
+
type: 'suggestion',
|
531
|
+
docs: {
|
532
|
+
description: 'Disallow the use of then()/catch()/finally() when invoking specific functions.'
|
533
|
+
},
|
534
|
+
schema: [{
|
535
|
+
type: 'object',
|
536
|
+
properties: {
|
537
|
+
restrictedFunctions: {
|
538
|
+
type: 'array',
|
539
|
+
uniqueItems: true,
|
540
|
+
items: {
|
541
|
+
type: 'string'
|
542
|
+
}
|
543
|
+
}
|
544
|
+
}
|
545
|
+
}],
|
546
|
+
messages: {
|
547
|
+
forbiddenThenCatchFinally: "then()/catch()/finally() is forbidden when invoke {{ name }}()."
|
548
|
+
}
|
549
|
+
},
|
550
|
+
create(context) {
|
551
|
+
const configuration = context.options[0] || {};
|
552
|
+
const restrictedFunctions = configuration.restrictedFunctions || [];
|
553
|
+
function isTopLevelScoped() {
|
554
|
+
return context.getScope().block.type === 'Program';
|
555
|
+
}
|
556
|
+
function isThenCatchFinally(node) {
|
557
|
+
return node.property && (node.property.name === 'then' || node.property.name === 'catch' || node.property.name === 'finally');
|
558
|
+
}
|
559
|
+
return {
|
560
|
+
'CallExpression > MemberExpression.callee'(node) {
|
561
|
+
if (isTopLevelScoped()) {
|
562
|
+
return;
|
563
|
+
}
|
564
|
+
if (!isThenCatchFinally(node)) {
|
565
|
+
return;
|
566
|
+
}
|
567
|
+
const callExpression = node.object;
|
568
|
+
if (callExpression.type === 'CallExpression' && callExpression.callee.type === 'Identifier' && restrictedFunctions.includes(callExpression.callee.name)) {
|
569
|
+
context.report({
|
570
|
+
node: node.property,
|
571
|
+
messageId: 'forbiddenThenCatchFinally',
|
572
|
+
data: {
|
573
|
+
name: callExpression.callee.name
|
574
|
+
}
|
575
|
+
});
|
576
|
+
}
|
577
|
+
}
|
578
|
+
};
|
579
|
+
}
|
580
|
+
};
|
581
|
+
|
582
|
+
var noUnnecessaryTemplateLiterals = {
|
583
|
+
meta: {
|
584
|
+
type: 'problem',
|
585
|
+
docs: {
|
586
|
+
description: 'Check if a template string contains only one ${}',
|
587
|
+
recommended: true
|
588
|
+
},
|
589
|
+
fixable: 'code',
|
590
|
+
schema: [],
|
591
|
+
messages: {
|
592
|
+
unnecessaryTemplateString: 'Unnecessary template string with only one ${}.'
|
593
|
+
}
|
594
|
+
},
|
595
|
+
create(context) {
|
596
|
+
return {
|
597
|
+
TemplateLiteral(node) {
|
598
|
+
const code = context.sourceCode.getText(node);
|
599
|
+
if (code.startsWith('`${') && code.endsWith('}`') && code.split('${').length === 2) {
|
600
|
+
context.report({
|
601
|
+
node,
|
602
|
+
messageId: 'unnecessaryTemplateString',
|
603
|
+
fix(fixer) {
|
604
|
+
return fixer.replaceText(node, code.substring(3, code.length - 2));
|
605
|
+
}
|
606
|
+
});
|
607
|
+
}
|
608
|
+
}
|
609
|
+
};
|
610
|
+
}
|
611
|
+
};
|
612
|
+
|
613
|
+
var reactBetterExhaustiveDeps = {
|
533
614
|
meta: {
|
534
615
|
type: 'suggestion',
|
535
616
|
docs: {
|
536
|
-
description:
|
537
|
-
'verifies the list of dependencies for Hooks like useEffect and similar',
|
617
|
+
description: 'verifies the list of dependencies for Hooks like useEffect and similar',
|
538
618
|
url: 'https://github.com/facebook/react/issues/14920'
|
539
619
|
},
|
540
620
|
fixable: 'code',
|
541
621
|
hasSuggestions: true,
|
542
|
-
schema: [
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
additionalProperties: {
|
570
|
-
type: 'boolean'
|
571
|
-
}
|
572
|
-
}
|
573
|
-
]
|
574
|
-
}
|
575
|
-
},
|
576
|
-
checkMemoizedVariableIsStatic: {
|
577
|
-
type: 'boolean'
|
622
|
+
schema: [{
|
623
|
+
type: 'object',
|
624
|
+
additionalProperties: false,
|
625
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: false,
|
626
|
+
properties: {
|
627
|
+
additionalHooks: {
|
628
|
+
type: 'string'
|
629
|
+
},
|
630
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: {
|
631
|
+
type: 'boolean'
|
632
|
+
},
|
633
|
+
staticHooks: {
|
634
|
+
type: 'object',
|
635
|
+
additionalProperties: {
|
636
|
+
oneOf: [{
|
637
|
+
type: 'boolean'
|
638
|
+
}, {
|
639
|
+
type: 'array',
|
640
|
+
items: {
|
641
|
+
type: 'boolean'
|
642
|
+
}
|
643
|
+
}, {
|
644
|
+
type: 'object',
|
645
|
+
additionalProperties: {
|
646
|
+
type: 'boolean'
|
647
|
+
}
|
648
|
+
}]
|
578
649
|
}
|
650
|
+
},
|
651
|
+
checkMemoizedVariableIsStatic: {
|
652
|
+
type: 'boolean'
|
579
653
|
}
|
580
654
|
}
|
581
|
-
]
|
655
|
+
}]
|
582
656
|
},
|
583
657
|
create(context) {
|
584
|
-
const additionalHooks =
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
? new RegExp(context.options[0].additionalHooks)
|
589
|
-
: undefined;
|
590
|
-
const enableDangerousAutofixThisMayCauseInfiniteLoops =
|
591
|
-
(context.options &&
|
592
|
-
context.options[0] &&
|
593
|
-
context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops) ||
|
594
|
-
false;
|
595
|
-
const staticHooks =
|
596
|
-
(context.options &&
|
597
|
-
context.options[0] &&
|
598
|
-
context.options[0].staticHooks) ||
|
599
|
-
{};
|
600
|
-
const checkMemoizedVariableIsStatic =
|
601
|
-
(context.options &&
|
602
|
-
context.options[0] &&
|
603
|
-
context.options[0].checkMemoizedVariableIsStatic) ||
|
604
|
-
false;
|
658
|
+
const additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : undefined;
|
659
|
+
const enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
|
660
|
+
const staticHooks = context.options && context.options[0] && context.options[0].staticHooks || {};
|
661
|
+
const checkMemoizedVariableIsStatic = context.options && context.options[0] && context.options[0].checkMemoizedVariableIsStatic || false;
|
605
662
|
const options = {
|
606
663
|
additionalHooks,
|
607
664
|
enableDangerousAutofixThisMayCauseInfiniteLoops,
|
@@ -609,11 +666,8 @@ var betterExhaustiveDeps = {
|
|
609
666
|
checkMemoizedVariableIsStatic
|
610
667
|
};
|
611
668
|
function reportProblem(problem) {
|
612
|
-
if (
|
613
|
-
|
614
|
-
Array.isArray(problem.suggest) &&
|
615
|
-
problem.suggest.length > 0
|
616
|
-
) {
|
669
|
+
if (enableDangerousAutofixThisMayCauseInfiniteLoops &&
|
670
|
+
Array.isArray(problem.suggest) && problem.suggest.length > 0) {
|
617
671
|
problem.fix = problem.suggest[0].fix;
|
618
672
|
}
|
619
673
|
context.report(problem);
|
@@ -633,28 +687,11 @@ var betterExhaustiveDeps = {
|
|
633
687
|
return result;
|
634
688
|
};
|
635
689
|
}
|
636
|
-
function visitFunctionWithDependencies(
|
637
|
-
node,
|
638
|
-
declaredDependenciesNode,
|
639
|
-
reactiveHook,
|
640
|
-
reactiveHookName,
|
641
|
-
isEffect
|
642
|
-
) {
|
690
|
+
function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
|
643
691
|
if (isEffect && node.async) {
|
644
692
|
reportProblem({
|
645
693
|
node: node,
|
646
|
-
message:
|
647
|
-
`Effect callbacks are synchronous to prevent race conditions. ` +
|
648
|
-
`Put the async function inside:\n\n` +
|
649
|
-
'useEffect(() => {\n' +
|
650
|
-
' async function fetchData() {\n' +
|
651
|
-
' // You can await here\n' +
|
652
|
-
' const response = await MyAPI.getData(someId);\n' +
|
653
|
-
' // ...\n' +
|
654
|
-
' }\n' +
|
655
|
-
' fetchData();\n' +
|
656
|
-
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
|
657
|
-
'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching'
|
694
|
+
message: "Effect callbacks are synchronous to prevent race conditions. " + "Put the async function inside:\n\n" + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching'
|
658
695
|
});
|
659
696
|
}
|
660
697
|
const scope = scopeManager.acquire(node);
|
@@ -675,14 +712,8 @@ var betterExhaustiveDeps = {
|
|
675
712
|
componentScope = currentScope;
|
676
713
|
}
|
677
714
|
const isArray = Array.isArray;
|
678
|
-
const memoizedIsStableKnownHookValue = memoizeWithWeakMap(
|
679
|
-
|
680
|
-
stableKnownValueCache
|
681
|
-
);
|
682
|
-
const memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(
|
683
|
-
isFunctionWithoutCapturedValues,
|
684
|
-
functionWithoutCapturedValueCache
|
685
|
-
);
|
715
|
+
const memoizedIsStableKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
|
716
|
+
const memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache);
|
686
717
|
function isStableKnownHookValue(resolved) {
|
687
718
|
if (!isArray(resolved.defs)) {
|
688
719
|
return false;
|
@@ -709,40 +740,27 @@ var betterExhaustiveDeps = {
|
|
709
740
|
return false;
|
710
741
|
}
|
711
742
|
}
|
712
|
-
if (
|
713
|
-
declaration.kind === 'const' &&
|
714
|
-
init.type === 'Literal' &&
|
715
|
-
(typeof init.value === 'string' ||
|
716
|
-
typeof init.value === 'number' ||
|
717
|
-
init.value == null)
|
718
|
-
) {
|
743
|
+
if (declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || typeof init.value === 'number' || init.value == null)) {
|
719
744
|
return true;
|
720
745
|
}
|
721
746
|
if (init.type !== 'CallExpression') {
|
722
747
|
return false;
|
723
748
|
}
|
724
749
|
let callee = init.callee;
|
725
|
-
if (
|
726
|
-
callee.type === 'MemberExpression' &&
|
727
|
-
callee.object.name === 'React' &&
|
728
|
-
callee.property != null &&
|
729
|
-
!callee.computed
|
730
|
-
) {
|
750
|
+
if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property != null && !callee.computed) {
|
731
751
|
callee = callee.property;
|
732
752
|
}
|
733
753
|
if (callee.type !== 'Identifier') {
|
734
754
|
return false;
|
735
755
|
}
|
736
756
|
const id = def.node.id;
|
737
|
-
const {
|
757
|
+
const {
|
758
|
+
name
|
759
|
+
} = callee;
|
738
760
|
if (name === 'useRef' && id.type === 'Identifier') {
|
739
761
|
return true;
|
740
762
|
} else if (name === 'useState' || name === 'useReducer') {
|
741
|
-
if (
|
742
|
-
id.type === 'ArrayPattern' &&
|
743
|
-
id.elements.length === 2 &&
|
744
|
-
isArray(resolved.identifiers)
|
745
|
-
) {
|
763
|
+
if (id.type === 'ArrayPattern' && id.elements.length === 2 && isArray(resolved.identifiers)) {
|
746
764
|
if (id.elements[1] === resolved.identifiers[0]) {
|
747
765
|
if (name === 'useState') {
|
748
766
|
const references = resolved.references;
|
@@ -769,18 +787,11 @@ var betterExhaustiveDeps = {
|
|
769
787
|
}
|
770
788
|
}
|
771
789
|
} else if (name === 'useTransition') {
|
772
|
-
if (
|
773
|
-
|
774
|
-
id.elements.length === 2 &&
|
775
|
-
Array.isArray(resolved.identifiers) &&
|
776
|
-
id.elements[1] === resolved.identifiers[0]
|
777
|
-
) {
|
790
|
+
if (id.type === 'ArrayPattern' && id.elements.length === 2 && Array.isArray(resolved.identifiers) &&
|
791
|
+
id.elements[1] === resolved.identifiers[0]) {
|
778
792
|
return true;
|
779
793
|
}
|
780
|
-
} else if (
|
781
|
-
options.checkMemoizedVariableIsStatic &&
|
782
|
-
(name === 'useMemo' || name === 'useCallback')
|
783
|
-
) {
|
794
|
+
} else if (options.checkMemoizedVariableIsStatic && (name === 'useMemo' || name === 'useCallback')) {
|
784
795
|
const hookArgs = callee.parent.arguments;
|
785
796
|
if (hookArgs.length < 2) {
|
786
797
|
return false;
|
@@ -790,13 +801,8 @@ var betterExhaustiveDeps = {
|
|
790
801
|
return true;
|
791
802
|
}
|
792
803
|
for (const dependencyNode of dependencies) {
|
793
|
-
const dependencyRefernece = resolved.scope.references.find(
|
794
|
-
|
795
|
-
);
|
796
|
-
if (
|
797
|
-
dependencyRefernece !== undefined &&
|
798
|
-
memoizedIsStableKnownHookValue(dependencyRefernece.resolved)
|
799
|
-
) {
|
804
|
+
const dependencyRefernece = resolved.scope.references.find(reference => reference.identifier === dependencyNode);
|
805
|
+
if (dependencyRefernece !== undefined && memoizedIsStableKnownHookValue(dependencyRefernece.resolved)) {
|
800
806
|
continue;
|
801
807
|
} else {
|
802
808
|
return false;
|
@@ -805,11 +811,7 @@ var betterExhaustiveDeps = {
|
|
805
811
|
return true;
|
806
812
|
} else {
|
807
813
|
Object.entries(options.staticHooks).forEach(([key, staticParts]) => {
|
808
|
-
if (
|
809
|
-
typeof staticParts === 'object' &&
|
810
|
-
staticParts.regexp &&
|
811
|
-
new RegExp(key).test(name)
|
812
|
-
) {
|
814
|
+
if (typeof staticParts === 'object' && staticParts.regexp && new RegExp(key).test(name)) {
|
813
815
|
options.staticHooks[name] = staticParts.value;
|
814
816
|
}
|
815
817
|
});
|
@@ -818,23 +820,14 @@ var betterExhaustiveDeps = {
|
|
818
820
|
if (staticParts === true) {
|
819
821
|
return true;
|
820
822
|
} else if (Array.isArray(staticParts)) {
|
821
|
-
if (
|
822
|
-
id.type === 'ArrayPattern' &&
|
823
|
-
id.elements.length <= staticParts.length &&
|
824
|
-
Array.isArray(resolved.identifiers)
|
825
|
-
) {
|
823
|
+
if (id.type === 'ArrayPattern' && id.elements.length <= staticParts.length && Array.isArray(resolved.identifiers)) {
|
826
824
|
const idx = id.elements.indexOf(resolved.identifiers[0]);
|
827
825
|
if (idx >= 0) {
|
828
826
|
return staticParts[idx];
|
829
827
|
}
|
830
828
|
}
|
831
|
-
} else if (
|
832
|
-
|
833
|
-
id.type === 'ObjectPattern'
|
834
|
-
) {
|
835
|
-
const property = id.properties.find(
|
836
|
-
p => p.key === resolved.identifiers[0]
|
837
|
-
);
|
829
|
+
} else if (typeof staticParts === 'object' && id.type === 'ObjectPattern') {
|
830
|
+
const property = id.properties.find(p => p.key === resolved.identifiers[0]);
|
838
831
|
if (property) {
|
839
832
|
return staticParts[property.key.name];
|
840
833
|
}
|
@@ -862,11 +855,8 @@ var betterExhaustiveDeps = {
|
|
862
855
|
const childScope = childScopes[i];
|
863
856
|
const childScopeBlock = childScope.block;
|
864
857
|
if (
|
865
|
-
|
866
|
-
|
867
|
-
(fnNode.type === 'VariableDeclarator' &&
|
868
|
-
childScopeBlock.parent === fnNode)
|
869
|
-
) {
|
858
|
+
fnNode.type === 'FunctionDeclaration' && childScopeBlock === fnNode ||
|
859
|
+
fnNode.type === 'VariableDeclarator' && childScopeBlock.parent === fnNode) {
|
870
860
|
fnScope = childScope;
|
871
861
|
break;
|
872
862
|
}
|
@@ -879,10 +869,8 @@ var betterExhaustiveDeps = {
|
|
879
869
|
if (ref.resolved == null) {
|
880
870
|
continue;
|
881
871
|
}
|
882
|
-
if (
|
883
|
-
|
884
|
-
!memoizedIsStableKnownHookValue(ref.resolved)
|
885
|
-
) {
|
872
|
+
if (pureScopes.has(ref.resolved.scope) &&
|
873
|
+
!memoizedIsStableKnownHookValue(ref.resolved)) {
|
886
874
|
return false;
|
887
875
|
}
|
888
876
|
}
|
@@ -894,9 +882,7 @@ var betterExhaustiveDeps = {
|
|
894
882
|
let isInReturnedFunction = false;
|
895
883
|
while (curScope.block !== node) {
|
896
884
|
if (curScope.type === 'function') {
|
897
|
-
isInReturnedFunction =
|
898
|
-
curScope.block.parent != null &&
|
899
|
-
curScope.block.parent.type === 'ReturnStatement';
|
885
|
+
isInReturnedFunction = curScope.block.parent != null && curScope.block.parent.type === 'ReturnStatement';
|
900
886
|
}
|
901
887
|
curScope = curScope.upper;
|
902
888
|
}
|
@@ -913,34 +899,19 @@ var betterExhaustiveDeps = {
|
|
913
899
|
if (!pureScopes.has(reference.resolved.scope)) {
|
914
900
|
continue;
|
915
901
|
}
|
916
|
-
const referenceNode = fastFindReferenceWithParent(
|
917
|
-
node,
|
918
|
-
reference.identifier
|
919
|
-
);
|
902
|
+
const referenceNode = fastFindReferenceWithParent(node, reference.identifier);
|
920
903
|
const dependencyNode = getDependency(referenceNode);
|
921
|
-
const dependency = analyzePropertyChain(
|
922
|
-
dependencyNode,
|
923
|
-
optionalChains
|
924
|
-
);
|
904
|
+
const dependency = analyzePropertyChain(dependencyNode, optionalChains);
|
925
905
|
if (
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
dependencyNode.parent.type === 'OptionalMemberExpression') &&
|
930
|
-
!dependencyNode.parent.computed &&
|
931
|
-
dependencyNode.parent.property.type === 'Identifier' &&
|
932
|
-
dependencyNode.parent.property.name === 'current' &&
|
933
|
-
isInsideEffectCleanup(reference)
|
934
|
-
) {
|
906
|
+
isEffect &&
|
907
|
+
dependencyNode.type === 'Identifier' && (dependencyNode.parent.type === 'MemberExpression' || dependencyNode.parent.type === 'OptionalMemberExpression') && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' &&
|
908
|
+
isInsideEffectCleanup(reference)) {
|
935
909
|
currentRefsInEffectCleanup.set(dependency, {
|
936
910
|
reference,
|
937
911
|
dependencyNode
|
938
912
|
});
|
939
913
|
}
|
940
|
-
if (
|
941
|
-
dependencyNode.parent.type === 'TSTypeQuery' ||
|
942
|
-
dependencyNode.parent.type === 'TSTypeReference'
|
943
|
-
) {
|
914
|
+
if (dependencyNode.parent.type === 'TSTypeQuery' || dependencyNode.parent.type === 'TSTypeReference') {
|
944
915
|
continue;
|
945
916
|
}
|
946
917
|
const def = reference.resolved.defs[0];
|
@@ -955,9 +926,7 @@ var betterExhaustiveDeps = {
|
|
955
926
|
}
|
956
927
|
if (!dependencies.has(dependency)) {
|
957
928
|
const resolved = reference.resolved;
|
958
|
-
const isStable =
|
959
|
-
memoizedIsStableKnownHookValue(resolved) ||
|
960
|
-
memoizedIsFunctionWithoutCapturedValues(resolved);
|
929
|
+
const isStable = memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
|
961
930
|
dependencies.set(dependency, {
|
962
931
|
isStable,
|
963
932
|
references: [reference]
|
@@ -970,39 +939,33 @@ var betterExhaustiveDeps = {
|
|
970
939
|
gatherDependenciesRecursively(childScope);
|
971
940
|
}
|
972
941
|
}
|
973
|
-
currentRefsInEffectCleanup.forEach(
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
}
|
991
|
-
}
|
992
|
-
if (foundCurrentAssignment) {
|
993
|
-
return;
|
942
|
+
currentRefsInEffectCleanup.forEach(({
|
943
|
+
reference,
|
944
|
+
dependencyNode
|
945
|
+
}, dependency) => {
|
946
|
+
const references = reference.resolved.references;
|
947
|
+
let foundCurrentAssignment = false;
|
948
|
+
for (const {
|
949
|
+
identifier
|
950
|
+
} of references) {
|
951
|
+
const {
|
952
|
+
parent
|
953
|
+
} = identifier;
|
954
|
+
if (parent != null &&
|
955
|
+
parent.type === 'MemberExpression' && !parent.computed && parent.property.type === 'Identifier' && parent.property.name === 'current' &&
|
956
|
+
parent.parent.type === 'AssignmentExpression' && parent.parent.left === parent) {
|
957
|
+
foundCurrentAssignment = true;
|
958
|
+
break;
|
994
959
|
}
|
995
|
-
reportProblem({
|
996
|
-
node: dependencyNode.parent.property,
|
997
|
-
message:
|
998
|
-
`The ref value '${dependency}.current' will likely have ` +
|
999
|
-
`changed by the time this effect cleanup function runs. If ` +
|
1000
|
-
`this ref points to a node rendered by React, copy ` +
|
1001
|
-
`'${dependency}.current' to a variable inside the effect, and ` +
|
1002
|
-
`use that variable in the cleanup function.`
|
1003
|
-
});
|
1004
960
|
}
|
1005
|
-
|
961
|
+
if (foundCurrentAssignment) {
|
962
|
+
return;
|
963
|
+
}
|
964
|
+
reportProblem({
|
965
|
+
node: dependencyNode.parent.property,
|
966
|
+
message: "The ref value '".concat(dependency, ".current' will likely have ") + "changed by the time this effect cleanup function runs. If " + "this ref points to a node rendered by React, copy " + "'".concat(dependency, ".current' to a variable inside the effect, and ") + "use that variable in the cleanup function."
|
967
|
+
});
|
968
|
+
});
|
1006
969
|
const staleAssignments = new Set();
|
1007
970
|
function reportStaleAssignment(writeExpr, key) {
|
1008
971
|
if (staleAssignments.has(key)) {
|
@@ -1011,17 +974,14 @@ var betterExhaustiveDeps = {
|
|
1011
974
|
staleAssignments.add(key);
|
1012
975
|
reportProblem({
|
1013
976
|
node: writeExpr,
|
1014
|
-
message:
|
1015
|
-
`Assignments to the '${key}' variable from inside React Hook ` +
|
1016
|
-
`${context.getSource(reactiveHook)} will be lost after each ` +
|
1017
|
-
`render. To preserve the value over time, store it in a useRef ` +
|
1018
|
-
`Hook and keep the mutable value in the '.current' property. ` +
|
1019
|
-
`Otherwise, you can move this variable directly inside ` +
|
1020
|
-
`${context.getSource(reactiveHook)}.`
|
977
|
+
message: "Assignments to the '".concat(key, "' variable from inside React Hook ") + "".concat(context.getSource(reactiveHook), " will be lost after each ") + "render. To preserve the value over time, store it in a useRef " + "Hook and keep the mutable value in the '.current' property. " + "Otherwise, you can move this variable directly inside " + "".concat(context.getSource(reactiveHook), ".")
|
1021
978
|
});
|
1022
979
|
}
|
1023
980
|
const stableDependencies = new Set();
|
1024
|
-
dependencies.forEach(({
|
981
|
+
dependencies.forEach(({
|
982
|
+
isStable,
|
983
|
+
references
|
984
|
+
}, key) => {
|
1025
985
|
if (isStable) {
|
1026
986
|
stableDependencies.add(key);
|
1027
987
|
}
|
@@ -1036,7 +996,9 @@ var betterExhaustiveDeps = {
|
|
1036
996
|
}
|
1037
997
|
if (!declaredDependenciesNode) {
|
1038
998
|
let setStateInsideEffectWithoutDeps = null;
|
1039
|
-
dependencies.forEach(({
|
999
|
+
dependencies.forEach(({
|
1000
|
+
references
|
1001
|
+
}, key) => {
|
1040
1002
|
if (setStateInsideEffectWithoutDeps) {
|
1041
1003
|
return;
|
1042
1004
|
}
|
@@ -1060,7 +1022,9 @@ var betterExhaustiveDeps = {
|
|
1060
1022
|
});
|
1061
1023
|
});
|
1062
1024
|
if (setStateInsideEffectWithoutDeps) {
|
1063
|
-
const {
|
1025
|
+
const {
|
1026
|
+
suggestedDependencies
|
1027
|
+
} = collectRecommendations({
|
1064
1028
|
dependencies,
|
1065
1029
|
declaredDependencies: [],
|
1066
1030
|
stableDependencies,
|
@@ -1069,25 +1033,13 @@ var betterExhaustiveDeps = {
|
|
1069
1033
|
});
|
1070
1034
|
reportProblem({
|
1071
1035
|
node: reactiveHook,
|
1072
|
-
message:
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
`] as a second argument to the ${reactiveHookName} Hook.`,
|
1078
|
-
suggest: [
|
1079
|
-
{
|
1080
|
-
desc: `Add dependencies array: [${suggestedDependencies.join(
|
1081
|
-
', '
|
1082
|
-
)}]`,
|
1083
|
-
fix(fixer) {
|
1084
|
-
return fixer.insertTextAfter(
|
1085
|
-
node,
|
1086
|
-
`, [${suggestedDependencies.join(', ')}]`
|
1087
|
-
);
|
1088
|
-
}
|
1036
|
+
message: "React Hook ".concat(reactiveHookName, " contains a call to '").concat(setStateInsideEffectWithoutDeps, "'. ") + "Without a list of dependencies, this can lead to an infinite chain of updates. " + "To fix this, pass [" + suggestedDependencies.join(', ') + "] as a second argument to the ".concat(reactiveHookName, " Hook."),
|
1037
|
+
suggest: [{
|
1038
|
+
desc: "Add dependencies array: [".concat(suggestedDependencies.join(', '), "]"),
|
1039
|
+
fix(fixer) {
|
1040
|
+
return fixer.insertTextAfter(node, ", [".concat(suggestedDependencies.join(', '), "]"));
|
1089
1041
|
}
|
1090
|
-
]
|
1042
|
+
}]
|
1091
1043
|
});
|
1092
1044
|
}
|
1093
1045
|
return;
|
@@ -1097,11 +1049,7 @@ var betterExhaustiveDeps = {
|
|
1097
1049
|
if (declaredDependenciesNode.type !== 'ArrayExpression') {
|
1098
1050
|
reportProblem({
|
1099
1051
|
node: declaredDependenciesNode,
|
1100
|
-
message:
|
1101
|
-
`React Hook ${context.getSource(reactiveHook)} was passed a ` +
|
1102
|
-
'dependency list that is not an array literal. This means we ' +
|
1103
|
-
"can't statically verify whether you've passed the correct " +
|
1104
|
-
'dependencies.'
|
1052
|
+
message: "React Hook ".concat(context.getSource(reactiveHook), " was passed a ") + 'dependency list that is not an array literal. This means we ' + "can't statically verify whether you've passed the correct " + 'dependencies.'
|
1105
1053
|
});
|
1106
1054
|
} else {
|
1107
1055
|
declaredDependenciesNode.elements.forEach(declaredDependencyNode => {
|
@@ -1111,46 +1059,31 @@ var betterExhaustiveDeps = {
|
|
1111
1059
|
if (declaredDependencyNode.type === 'SpreadElement') {
|
1112
1060
|
reportProblem({
|
1113
1061
|
node: declaredDependencyNode,
|
1114
|
-
message:
|
1115
|
-
`React Hook ${context.getSource(reactiveHook)} has a spread ` +
|
1116
|
-
"element in its dependency array. This means we can't " +
|
1117
|
-
"statically verify whether you've passed the " +
|
1118
|
-
'correct dependencies.'
|
1062
|
+
message: "React Hook ".concat(context.getSource(reactiveHook), " has a spread ") + "element in its dependency array. This means we can't " + "statically verify whether you've passed the " + 'correct dependencies.'
|
1119
1063
|
});
|
1120
1064
|
return;
|
1121
1065
|
}
|
1122
1066
|
let declaredDependency;
|
1123
1067
|
try {
|
1124
|
-
declaredDependency = analyzePropertyChain(
|
1125
|
-
declaredDependencyNode,
|
1126
|
-
null
|
1127
|
-
);
|
1068
|
+
declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
|
1128
1069
|
} catch (err) {
|
1129
1070
|
if (/Unsupported node type/.test(err.message)) {
|
1130
1071
|
if (declaredDependencyNode.type === 'Literal') {
|
1131
1072
|
if (dependencies.has(declaredDependencyNode.value)) {
|
1132
1073
|
reportProblem({
|
1133
1074
|
node: declaredDependencyNode,
|
1134
|
-
message:
|
1135
|
-
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
|
1136
|
-
`because it never changes. ` +
|
1137
|
-
`Did you mean to include ${declaredDependencyNode.value} in the array instead?`
|
1075
|
+
message: "The ".concat(declaredDependencyNode.raw, " literal is not a valid dependency ") + "because it never changes. " + "Did you mean to include ".concat(declaredDependencyNode.value, " in the array instead?")
|
1138
1076
|
});
|
1139
1077
|
} else {
|
1140
1078
|
reportProblem({
|
1141
1079
|
node: declaredDependencyNode,
|
1142
|
-
message:
|
1143
|
-
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
|
1144
|
-
'because it never changes. You can safely remove it.'
|
1080
|
+
message: "The ".concat(declaredDependencyNode.raw, " literal is not a valid dependency ") + 'because it never changes. You can safely remove it.'
|
1145
1081
|
});
|
1146
1082
|
}
|
1147
1083
|
} else {
|
1148
1084
|
reportProblem({
|
1149
1085
|
node: declaredDependencyNode,
|
1150
|
-
message:
|
1151
|
-
`React Hook ${context.getSource(reactiveHook)} has a ` +
|
1152
|
-
`complex expression in the dependency array. ` +
|
1153
|
-
'Extract it to a separate variable so it can be statically checked.'
|
1086
|
+
message: "React Hook ".concat(context.getSource(reactiveHook), " has a ") + "complex expression in the dependency array. " + 'Extract it to a separate variable so it can be statically checked.'
|
1154
1087
|
});
|
1155
1088
|
}
|
1156
1089
|
return;
|
@@ -1158,16 +1091,10 @@ var betterExhaustiveDeps = {
|
|
1158
1091
|
throw err;
|
1159
1092
|
}
|
1160
1093
|
let maybeID = declaredDependencyNode;
|
1161
|
-
while (
|
1162
|
-
maybeID.type === 'MemberExpression' ||
|
1163
|
-
maybeID.type === 'OptionalMemberExpression' ||
|
1164
|
-
maybeID.type === 'ChainExpression'
|
1165
|
-
) {
|
1094
|
+
while (maybeID.type === 'MemberExpression' || maybeID.type === 'OptionalMemberExpression' || maybeID.type === 'ChainExpression') {
|
1166
1095
|
maybeID = maybeID.object || maybeID.expression.object;
|
1167
1096
|
}
|
1168
|
-
const isDeclaredInComponent = !componentScope.through.some(
|
1169
|
-
ref => ref.identifier === maybeID
|
1170
|
-
);
|
1097
|
+
const isDeclaredInComponent = !componentScope.through.some(ref => ref.identifier === maybeID);
|
1171
1098
|
declaredDependencies.push({
|
1172
1099
|
key: declaredDependency,
|
1173
1100
|
node: declaredDependencyNode
|
@@ -1190,10 +1117,7 @@ var betterExhaustiveDeps = {
|
|
1190
1117
|
isEffect
|
1191
1118
|
});
|
1192
1119
|
let suggestedDeps = suggestedDependencies;
|
1193
|
-
const problemCount =
|
1194
|
-
duplicateDependencies.size +
|
1195
|
-
missingDependencies.size +
|
1196
|
-
unnecessaryDependencies.size;
|
1120
|
+
const problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
|
1197
1121
|
if (problemCount === 0) {
|
1198
1122
|
const constructions = scanForConstructions({
|
1199
1123
|
declaredDependencies,
|
@@ -1201,53 +1125,36 @@ var betterExhaustiveDeps = {
|
|
1201
1125
|
componentScope,
|
1202
1126
|
scope
|
1203
1127
|
});
|
1204
|
-
constructions.forEach(
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
) {
|
1228
|
-
suggest = [
|
1229
|
-
{
|
1230
|
-
desc: `Wrap the ${constructionType} of '${construction.name.name}' in its own ${wrapperHook}() Hook.`,
|
1231
|
-
fix(fixer) {
|
1232
|
-
const [before, after] =
|
1233
|
-
wrapperHook === 'useMemo'
|
1234
|
-
? [`useMemo(() => { return `, '; })']
|
1235
|
-
: ['useCallback(', ')'];
|
1236
|
-
return [
|
1237
|
-
fixer.insertTextBefore(construction.node.init, before),
|
1238
|
-
fixer.insertTextAfter(construction.node.init, after)
|
1239
|
-
];
|
1240
|
-
}
|
1241
|
-
}
|
1242
|
-
];
|
1243
|
-
}
|
1244
|
-
reportProblem({
|
1245
|
-
node: construction.node,
|
1246
|
-
message,
|
1247
|
-
suggest
|
1248
|
-
});
|
1128
|
+
constructions.forEach(({
|
1129
|
+
construction,
|
1130
|
+
isUsedOutsideOfHook,
|
1131
|
+
depType
|
1132
|
+
}) => {
|
1133
|
+
const wrapperHook = depType === 'function' ? 'useCallback' : 'useMemo';
|
1134
|
+
const constructionType = depType === 'function' ? 'definition' : 'initialization';
|
1135
|
+
const defaultAdvice = "wrap the ".concat(constructionType, " of '").concat(construction.name.name, "' in its own ").concat(wrapperHook, "() Hook.");
|
1136
|
+
const advice = isUsedOutsideOfHook ? "To fix this, ".concat(defaultAdvice) : "Move it inside the ".concat(reactiveHookName, " callback. Alternatively, ").concat(defaultAdvice);
|
1137
|
+
const causation = depType === 'conditional' || depType === 'logical expression' ? 'could make' : 'makes';
|
1138
|
+
const message = "The '".concat(construction.name.name, "' ").concat(depType, " ").concat(causation, " the dependencies of ") + "".concat(reactiveHookName, " Hook (at line ").concat(declaredDependenciesNode.loc.start.line, ") ") + "change on every render. ".concat(advice);
|
1139
|
+
let suggest;
|
1140
|
+
if (isUsedOutsideOfHook && construction.type === 'Variable' &&
|
1141
|
+
depType === 'function') {
|
1142
|
+
suggest = [{
|
1143
|
+
desc: "Wrap the ".concat(constructionType, " of '").concat(construction.name.name, "' in its own ").concat(wrapperHook, "() Hook."),
|
1144
|
+
fix(fixer) {
|
1145
|
+
const [before, after] = wrapperHook === 'useMemo' ? ["useMemo(() => { return ", '; })'] : ['useCallback(', ')'];
|
1146
|
+
return [
|
1147
|
+
fixer.insertTextBefore(construction.node.init, before),
|
1148
|
+
fixer.insertTextAfter(construction.node.init, after)];
|
1149
|
+
}
|
1150
|
+
}];
|
1249
1151
|
}
|
1250
|
-
|
1152
|
+
reportProblem({
|
1153
|
+
node: construction.node,
|
1154
|
+
message,
|
1155
|
+
suggest
|
1156
|
+
});
|
1157
|
+
});
|
1251
1158
|
return;
|
1252
1159
|
}
|
1253
1160
|
if (!isEffect && missingDependencies.size > 0) {
|
@@ -1287,19 +1194,7 @@ var betterExhaustiveDeps = {
|
|
1287
1194
|
if (deps.size === 0) {
|
1288
1195
|
return null;
|
1289
1196
|
}
|
1290
|
-
return (
|
1291
|
-
(deps.size > 1 ? '' : singlePrefix + ' ') +
|
1292
|
-
label +
|
1293
|
-
' ' +
|
1294
|
-
(deps.size > 1 ? 'dependencies' : 'dependency') +
|
1295
|
-
': ' +
|
1296
|
-
joinEnglish(
|
1297
|
-
[...deps].sort().map(name => "'" + formatDependency(name) + "'")
|
1298
|
-
) +
|
1299
|
-
`. Either ${fixVerb} ${
|
1300
|
-
deps.size > 1 ? 'them' : 'it'
|
1301
|
-
} or remove the dependency array.`
|
1302
|
-
);
|
1197
|
+
return (deps.size > 1 ? '' : singlePrefix + ' ') + label + ' ' + (deps.size > 1 ? 'dependencies' : 'dependency') + ': ' + joinEnglish([...deps].sort().map(name => "'" + formatDependency(name) + "'")) + ". Either ".concat(fixVerb, " ").concat(deps.size > 1 ? 'them' : 'it', " or remove the dependency array.");
|
1303
1198
|
}
|
1304
1199
|
let extraWarning = '';
|
1305
1200
|
if (unnecessaryDependencies.size > 0) {
|
@@ -1313,15 +1208,11 @@ var betterExhaustiveDeps = {
|
|
1313
1208
|
}
|
1314
1209
|
});
|
1315
1210
|
if (badRef != null) {
|
1316
|
-
extraWarning =
|
1317
|
-
` Mutable values like '${badRef}' aren't valid dependencies ` +
|
1318
|
-
"because mutating them doesn't re-render the component.";
|
1211
|
+
extraWarning = " Mutable values like '".concat(badRef, "' aren't valid dependencies ") + "because mutating them doesn't re-render the component.";
|
1319
1212
|
} else if (externalDependencies.size > 0) {
|
1320
1213
|
const dep = [...externalDependencies][0];
|
1321
1214
|
if (!scope.set.has(dep)) {
|
1322
|
-
extraWarning =
|
1323
|
-
` Outer scope values like '${dep}' aren't valid dependencies ` +
|
1324
|
-
`because mutating them doesn't re-render the component.`;
|
1215
|
+
extraWarning = " Outer scope values like '".concat(dep, "' aren't valid dependencies ") + "because mutating them doesn't re-render the component.";
|
1325
1216
|
}
|
1326
1217
|
}
|
1327
1218
|
}
|
@@ -1336,10 +1227,7 @@ var betterExhaustiveDeps = {
|
|
1336
1227
|
}
|
1337
1228
|
let isPropsOnlyUsedInMembers = true;
|
1338
1229
|
for (const ref of refs) {
|
1339
|
-
const id = fastFindReferenceWithParent(
|
1340
|
-
componentScope.block,
|
1341
|
-
ref.identifier
|
1342
|
-
);
|
1230
|
+
const id = fastFindReferenceWithParent(componentScope.block, ref.identifier);
|
1343
1231
|
if (!id) {
|
1344
1232
|
isPropsOnlyUsedInMembers = false;
|
1345
1233
|
break;
|
@@ -1349,20 +1237,13 @@ var betterExhaustiveDeps = {
|
|
1349
1237
|
isPropsOnlyUsedInMembers = false;
|
1350
1238
|
break;
|
1351
1239
|
}
|
1352
|
-
if (
|
1353
|
-
parent.type !== 'MemberExpression' &&
|
1354
|
-
parent.type !== 'OptionalMemberExpression'
|
1355
|
-
) {
|
1240
|
+
if (parent.type !== 'MemberExpression' && parent.type !== 'OptionalMemberExpression') {
|
1356
1241
|
isPropsOnlyUsedInMembers = false;
|
1357
1242
|
break;
|
1358
1243
|
}
|
1359
1244
|
}
|
1360
1245
|
if (isPropsOnlyUsedInMembers) {
|
1361
|
-
extraWarning =
|
1362
|
-
` However, 'props' will change when *any* prop changes, so the ` +
|
1363
|
-
`preferred fix is to destructure the 'props' object outside of ` +
|
1364
|
-
`the ${reactiveHookName} call and refer to those specific props ` +
|
1365
|
-
`inside ${context.getSource(reactiveHook)}.`;
|
1246
|
+
extraWarning = " However, 'props' will change when *any* prop changes, so the " + "preferred fix is to destructure the 'props' object outside of " + "the ".concat(reactiveHookName, " call and refer to those specific props ") + "inside ".concat(context.getSource(reactiveHook), ".");
|
1366
1247
|
}
|
1367
1248
|
}
|
1368
1249
|
if (!extraWarning && missingDependencies.size > 0) {
|
@@ -1384,13 +1265,7 @@ var betterExhaustiveDeps = {
|
|
1384
1265
|
let id;
|
1385
1266
|
for (let i = 0; i < usedDep.references.length; i++) {
|
1386
1267
|
id = usedDep.references[i].identifier;
|
1387
|
-
if (
|
1388
|
-
id != null &&
|
1389
|
-
id.parent != null &&
|
1390
|
-
(id.parent.type === 'CallExpression' ||
|
1391
|
-
id.parent.type === 'OptionalCallExpression') &&
|
1392
|
-
id.parent.callee === id
|
1393
|
-
) {
|
1268
|
+
if (id != null && id.parent != null && (id.parent.type === 'CallExpression' || id.parent.type === 'OptionalCallExpression') && id.parent.callee === id) {
|
1394
1269
|
isFunctionCall = true;
|
1395
1270
|
break;
|
1396
1271
|
}
|
@@ -1401,10 +1276,7 @@ var betterExhaustiveDeps = {
|
|
1401
1276
|
missingCallbackDep = missingDep;
|
1402
1277
|
});
|
1403
1278
|
if (missingCallbackDep != null) {
|
1404
|
-
extraWarning =
|
1405
|
-
` If '${missingCallbackDep}' changes too often, ` +
|
1406
|
-
`find the parent component that defines it ` +
|
1407
|
-
`and wrap that definition in useCallback.`;
|
1279
|
+
extraWarning = " If '".concat(missingCallbackDep, "' changes too often, ") + "find the parent component that defines it " + "and wrap that definition in useCallback.";
|
1408
1280
|
}
|
1409
1281
|
}
|
1410
1282
|
if (!extraWarning && missingDependencies.size > 0) {
|
@@ -1422,9 +1294,7 @@ var betterExhaustiveDeps = {
|
|
1422
1294
|
maybeCall = id.parent;
|
1423
1295
|
while (maybeCall != null && maybeCall !== componentScope.block) {
|
1424
1296
|
if (maybeCall.type === 'CallExpression') {
|
1425
|
-
const correspondingStateVariable = setStateCallSites.get(
|
1426
|
-
maybeCall.callee
|
1427
|
-
);
|
1297
|
+
const correspondingStateVariable = setStateCallSites.get(maybeCall.callee);
|
1428
1298
|
if (correspondingStateVariable != null) {
|
1429
1299
|
if (correspondingStateVariable.name === missingDep) {
|
1430
1300
|
setStateRecommendation = {
|
@@ -1464,27 +1334,13 @@ var betterExhaustiveDeps = {
|
|
1464
1334
|
if (setStateRecommendation != null) {
|
1465
1335
|
switch (setStateRecommendation.form) {
|
1466
1336
|
case 'reducer':
|
1467
|
-
extraWarning =
|
1468
|
-
` You can also replace multiple useState variables with useReducer ` +
|
1469
|
-
`if '${setStateRecommendation.setter}' needs the ` +
|
1470
|
-
`current value of '${setStateRecommendation.missingDep}'.`;
|
1337
|
+
extraWarning = " You can also replace multiple useState variables with useReducer " + "if '".concat(setStateRecommendation.setter, "' needs the ") + "current value of '".concat(setStateRecommendation.missingDep, "'.");
|
1471
1338
|
break;
|
1472
1339
|
case 'inlineReducer':
|
1473
|
-
extraWarning =
|
1474
|
-
` If '${setStateRecommendation.setter}' needs the ` +
|
1475
|
-
`current value of '${setStateRecommendation.missingDep}', ` +
|
1476
|
-
`you can also switch to useReducer instead of useState and ` +
|
1477
|
-
`read '${setStateRecommendation.missingDep}' in the reducer.`;
|
1340
|
+
extraWarning = " If '".concat(setStateRecommendation.setter, "' needs the ") + "current value of '".concat(setStateRecommendation.missingDep, "', ") + "you can also switch to useReducer instead of useState and " + "read '".concat(setStateRecommendation.missingDep, "' in the reducer.");
|
1478
1341
|
break;
|
1479
1342
|
case 'updater':
|
1480
|
-
extraWarning =
|
1481
|
-
setStateRecommendation.setter
|
1482
|
-
}(${setStateRecommendation.missingDep.slice(
|
1483
|
-
0,
|
1484
|
-
1
|
1485
|
-
)} => ...)' if you only need '${
|
1486
|
-
setStateRecommendation.missingDep
|
1487
|
-
}' in the '${setStateRecommendation.setter}' call.`;
|
1343
|
+
extraWarning = " You can also do a functional update '".concat(setStateRecommendation.setter, "(").concat(setStateRecommendation.missingDep.slice(0, 1), " => ...)' if you only need '").concat(setStateRecommendation.missingDep, "' in the '").concat(setStateRecommendation.setter, "' call.");
|
1488
1344
|
break;
|
1489
1345
|
default:
|
1490
1346
|
throw new Error('Unknown case.');
|
@@ -1493,35 +1349,14 @@ var betterExhaustiveDeps = {
|
|
1493
1349
|
}
|
1494
1350
|
reportProblem({
|
1495
1351
|
node: declaredDependenciesNode,
|
1496
|
-
message:
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
) ||
|
1505
|
-
getWarningMessage(
|
1506
|
-
duplicateDependencies,
|
1507
|
-
'a',
|
1508
|
-
'duplicate',
|
1509
|
-
'omit'
|
1510
|
-
)) +
|
1511
|
-
extraWarning,
|
1512
|
-
suggest: [
|
1513
|
-
{
|
1514
|
-
desc: `Update the dependencies array to be: [${suggestedDeps
|
1515
|
-
.map(element => formatDependency(element))
|
1516
|
-
.join(', ')}]`,
|
1517
|
-
fix(fixer) {
|
1518
|
-
return fixer.replaceText(
|
1519
|
-
declaredDependenciesNode,
|
1520
|
-
`[${suggestedDeps.map(element => formatDependency(element)).join(', ')}]`
|
1521
|
-
);
|
1522
|
-
}
|
1523
|
-
}
|
1524
|
-
]
|
1352
|
+
message: "React Hook ".concat(context.getSource(reactiveHook), " has ") + (
|
1353
|
+
getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') || getWarningMessage(duplicateDependencies, 'a', 'duplicate', 'omit')) + extraWarning,
|
1354
|
+
suggest: [{
|
1355
|
+
desc: "Update the dependencies array to be: [".concat(suggestedDeps.map(element => formatDependency(element)).join(', '), "]"),
|
1356
|
+
fix(fixer) {
|
1357
|
+
return fixer.replaceText(declaredDependenciesNode, "[".concat(suggestedDeps.map(element => formatDependency(element)).join(', '), "]"));
|
1358
|
+
}
|
1359
|
+
}]
|
1525
1360
|
});
|
1526
1361
|
}
|
1527
1362
|
function visitCallExpression(node) {
|
@@ -1537,23 +1372,15 @@ var betterExhaustiveDeps = {
|
|
1537
1372
|
if (!callback) {
|
1538
1373
|
reportProblem({
|
1539
1374
|
node: reactiveHook,
|
1540
|
-
message:
|
1541
|
-
`React Hook ${reactiveHookName} requires an effect callback. ` +
|
1542
|
-
`Did you forget to pass a callback to the hook?`
|
1375
|
+
message: "React Hook ".concat(reactiveHookName, " requires an effect callback. ") + "Did you forget to pass a callback to the hook?"
|
1543
1376
|
});
|
1544
1377
|
return;
|
1545
1378
|
}
|
1546
1379
|
if (!declaredDependenciesNode && !isEffect) {
|
1547
|
-
if (
|
1548
|
-
reactiveHookName === 'useMemo' ||
|
1549
|
-
reactiveHookName === 'useCallback'
|
1550
|
-
) {
|
1380
|
+
if (reactiveHookName === 'useMemo' || reactiveHookName === 'useCallback') {
|
1551
1381
|
reportProblem({
|
1552
1382
|
node: reactiveHook,
|
1553
|
-
message:
|
1554
|
-
`React Hook ${reactiveHookName} does nothing when called with ` +
|
1555
|
-
`only one argument. Did you forget to pass an array of ` +
|
1556
|
-
`dependencies?`
|
1383
|
+
message: "React Hook ".concat(reactiveHookName, " does nothing when called with ") + "only one argument. Did you forget to pass an array of " + "dependencies?"
|
1557
1384
|
});
|
1558
1385
|
}
|
1559
1386
|
return;
|
@@ -1561,24 +1388,13 @@ var betterExhaustiveDeps = {
|
|
1561
1388
|
switch (callback.type) {
|
1562
1389
|
case 'FunctionExpression':
|
1563
1390
|
case 'ArrowFunctionExpression':
|
1564
|
-
visitFunctionWithDependencies(
|
1565
|
-
callback,
|
1566
|
-
declaredDependenciesNode,
|
1567
|
-
reactiveHook,
|
1568
|
-
reactiveHookName,
|
1569
|
-
isEffect
|
1570
|
-
);
|
1391
|
+
visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1571
1392
|
return;
|
1572
1393
|
case 'Identifier':
|
1573
1394
|
if (!declaredDependenciesNode) {
|
1574
1395
|
return;
|
1575
1396
|
}
|
1576
|
-
if (
|
1577
|
-
declaredDependenciesNode.elements &&
|
1578
|
-
declaredDependenciesNode.elements.some(
|
1579
|
-
el => el && el.type === 'Identifier' && el.name === callback.name
|
1580
|
-
)
|
1581
|
-
) {
|
1397
|
+
if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some(el => el && el.type === 'Identifier' && el.name === callback.name)) {
|
1582
1398
|
return;
|
1583
1399
|
}
|
1584
1400
|
const variable = context.getScope().set.get(callback.name);
|
@@ -1594,13 +1410,7 @@ var betterExhaustiveDeps = {
|
|
1594
1410
|
}
|
1595
1411
|
switch (def.node.type) {
|
1596
1412
|
case 'FunctionDeclaration':
|
1597
|
-
visitFunctionWithDependencies(
|
1598
|
-
def.node,
|
1599
|
-
declaredDependenciesNode,
|
1600
|
-
reactiveHook,
|
1601
|
-
reactiveHookName,
|
1602
|
-
isEffect
|
1603
|
-
);
|
1413
|
+
visitFunctionWithDependencies(def.node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1604
1414
|
return;
|
1605
1415
|
case 'VariableDeclarator':
|
1606
1416
|
const init = def.node.init;
|
@@ -1610,13 +1420,7 @@ var betterExhaustiveDeps = {
|
|
1610
1420
|
switch (init.type) {
|
1611
1421
|
case 'ArrowFunctionExpression':
|
1612
1422
|
case 'FunctionExpression':
|
1613
|
-
visitFunctionWithDependencies(
|
1614
|
-
init,
|
1615
|
-
declaredDependenciesNode,
|
1616
|
-
reactiveHook,
|
1617
|
-
reactiveHookName,
|
1618
|
-
isEffect
|
1619
|
-
);
|
1423
|
+
visitFunctionWithDependencies(init, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1620
1424
|
return;
|
1621
1425
|
}
|
1622
1426
|
break;
|
@@ -1625,28 +1429,19 @@ var betterExhaustiveDeps = {
|
|
1625
1429
|
default:
|
1626
1430
|
reportProblem({
|
1627
1431
|
node: reactiveHook,
|
1628
|
-
message:
|
1629
|
-
`React Hook ${reactiveHookName} received a function whose dependencies ` +
|
1630
|
-
`are unknown. Pass an inline function instead.`
|
1432
|
+
message: "React Hook ".concat(reactiveHookName, " received a function whose dependencies ") + "are unknown. Pass an inline function instead."
|
1631
1433
|
});
|
1632
1434
|
return;
|
1633
1435
|
}
|
1634
1436
|
reportProblem({
|
1635
1437
|
node: reactiveHook,
|
1636
|
-
message:
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
desc: `Update the dependencies array to be: [${callback.name}]`,
|
1642
|
-
fix(fixer) {
|
1643
|
-
return fixer.replaceText(
|
1644
|
-
declaredDependenciesNode,
|
1645
|
-
`[${callback.name}]`
|
1646
|
-
);
|
1647
|
-
}
|
1438
|
+
message: "React Hook ".concat(reactiveHookName, " has a missing dependency: '").concat(callback.name, "'. ") + "Either include it or remove the dependency array.",
|
1439
|
+
suggest: [{
|
1440
|
+
desc: "Update the dependencies array to be: [".concat(callback.name, "]"),
|
1441
|
+
fix(fixer) {
|
1442
|
+
return fixer.replaceText(declaredDependenciesNode, "[".concat(callback.name, "]"));
|
1648
1443
|
}
|
1649
|
-
]
|
1444
|
+
}]
|
1650
1445
|
});
|
1651
1446
|
}
|
1652
1447
|
return {
|
@@ -1677,7 +1472,9 @@ function collectRecommendations({
|
|
1677
1472
|
parent.isSubtreeUsed = true;
|
1678
1473
|
});
|
1679
1474
|
});
|
1680
|
-
declaredDependencies.forEach(({
|
1475
|
+
declaredDependencies.forEach(({
|
1476
|
+
key
|
1477
|
+
}) => {
|
1681
1478
|
const node = getOrCreateNodeByPath(depTree, key);
|
1682
1479
|
node.isSatisfiedRecursively = true;
|
1683
1480
|
});
|
@@ -1712,12 +1509,7 @@ function collectRecommendations({
|
|
1712
1509
|
}
|
1713
1510
|
const missingDependencies = new Set();
|
1714
1511
|
const satisfyingDependencies = new Set();
|
1715
|
-
scanTreeRecursively(
|
1716
|
-
depTree,
|
1717
|
-
missingDependencies,
|
1718
|
-
satisfyingDependencies,
|
1719
|
-
key => key
|
1720
|
-
);
|
1512
|
+
scanTreeRecursively(depTree, missingDependencies, satisfyingDependencies, key => key);
|
1721
1513
|
function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
|
1722
1514
|
node.children.forEach((child, key) => {
|
1723
1515
|
const path = keyToPath(key);
|
@@ -1731,29 +1523,22 @@ function collectRecommendations({
|
|
1731
1523
|
missingPaths.add(path);
|
1732
1524
|
return;
|
1733
1525
|
}
|
1734
|
-
scanTreeRecursively(
|
1735
|
-
child,
|
1736
|
-
missingPaths,
|
1737
|
-
satisfyingPaths,
|
1738
|
-
childKey => path + '.' + childKey
|
1739
|
-
);
|
1526
|
+
scanTreeRecursively(child, missingPaths, satisfyingPaths, childKey => path + '.' + childKey);
|
1740
1527
|
});
|
1741
1528
|
}
|
1742
1529
|
const suggestedDependencies = [];
|
1743
1530
|
const unnecessaryDependencies = new Set();
|
1744
1531
|
const duplicateDependencies = new Set();
|
1745
|
-
declaredDependencies.forEach(({
|
1532
|
+
declaredDependencies.forEach(({
|
1533
|
+
key
|
1534
|
+
}) => {
|
1746
1535
|
if (satisfyingDependencies.has(key)) {
|
1747
1536
|
if (!suggestedDependencies.includes(key)) {
|
1748
1537
|
suggestedDependencies.push(key);
|
1749
1538
|
} else {
|
1750
1539
|
duplicateDependencies.add(key);
|
1751
1540
|
}
|
1752
|
-
} else if (
|
1753
|
-
isEffect &&
|
1754
|
-
!key.endsWith('.current') &&
|
1755
|
-
!externalDependencies.has(key)
|
1756
|
-
) {
|
1541
|
+
} else if (isEffect && !key.endsWith('.current') && !externalDependencies.has(key)) {
|
1757
1542
|
if (!suggestedDependencies.includes(key)) {
|
1758
1543
|
suggestedDependencies.push(key);
|
1759
1544
|
}
|
@@ -1783,18 +1568,12 @@ function getConstructionExpressionType(node) {
|
|
1783
1568
|
case 'ClassExpression':
|
1784
1569
|
return 'class';
|
1785
1570
|
case 'ConditionalExpression':
|
1786
|
-
if (
|
1787
|
-
getConstructionExpressionType(node.consequent) != null ||
|
1788
|
-
getConstructionExpressionType(node.alternate) != null
|
1789
|
-
) {
|
1571
|
+
if (getConstructionExpressionType(node.consequent) != null || getConstructionExpressionType(node.alternate) != null) {
|
1790
1572
|
return 'conditional';
|
1791
1573
|
}
|
1792
1574
|
return null;
|
1793
1575
|
case 'LogicalExpression':
|
1794
|
-
if (
|
1795
|
-
getConstructionExpressionType(node.left) != null ||
|
1796
|
-
getConstructionExpressionType(node.right) != null
|
1797
|
-
) {
|
1576
|
+
if (getConstructionExpressionType(node.left) != null || getConstructionExpressionType(node.right) != null) {
|
1798
1577
|
return 'logical expression';
|
1799
1578
|
}
|
1800
1579
|
return null;
|
@@ -1827,41 +1606,32 @@ function scanForConstructions({
|
|
1827
1606
|
componentScope,
|
1828
1607
|
scope
|
1829
1608
|
}) {
|
1830
|
-
const constructions = declaredDependencies
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
|
1835
|
-
}
|
1836
|
-
const node = ref.defs[0];
|
1837
|
-
if (node == null) {
|
1838
|
-
return null;
|
1839
|
-
}
|
1840
|
-
if (
|
1841
|
-
node.type === 'Variable' &&
|
1842
|
-
node.node.type === 'VariableDeclarator' &&
|
1843
|
-
node.node.id.type === 'Identifier' &&
|
1844
|
-
node.node.init != null
|
1845
|
-
) {
|
1846
|
-
const constantExpressionType = getConstructionExpressionType(
|
1847
|
-
node.node.init
|
1848
|
-
);
|
1849
|
-
if (constantExpressionType != null) {
|
1850
|
-
return [ref, constantExpressionType];
|
1851
|
-
}
|
1852
|
-
}
|
1853
|
-
if (
|
1854
|
-
node.type === 'FunctionName' &&
|
1855
|
-
node.node.type === 'FunctionDeclaration'
|
1856
|
-
) {
|
1857
|
-
return [ref, 'function'];
|
1858
|
-
}
|
1859
|
-
if (node.type === 'ClassName' && node.node.type === 'ClassDeclaration') {
|
1860
|
-
return [ref, 'class'];
|
1861
|
-
}
|
1609
|
+
const constructions = declaredDependencies.map(({
|
1610
|
+
key
|
1611
|
+
}) => {
|
1612
|
+
const ref = componentScope.variables.find(v => v.name === key);
|
1613
|
+
if (ref == null) {
|
1862
1614
|
return null;
|
1863
|
-
}
|
1864
|
-
.
|
1615
|
+
}
|
1616
|
+
const node = ref.defs[0];
|
1617
|
+
if (node == null) {
|
1618
|
+
return null;
|
1619
|
+
}
|
1620
|
+
if (node.type === 'Variable' && node.node.type === 'VariableDeclarator' && node.node.id.type === 'Identifier' &&
|
1621
|
+
node.node.init != null) {
|
1622
|
+
const constantExpressionType = getConstructionExpressionType(node.node.init);
|
1623
|
+
if (constantExpressionType != null) {
|
1624
|
+
return [ref, constantExpressionType];
|
1625
|
+
}
|
1626
|
+
}
|
1627
|
+
if (node.type === 'FunctionName' && node.node.type === 'FunctionDeclaration') {
|
1628
|
+
return [ref, 'function'];
|
1629
|
+
}
|
1630
|
+
if (node.type === 'ClassName' && node.node.type === 'ClassDeclaration') {
|
1631
|
+
return [ref, 'class'];
|
1632
|
+
}
|
1633
|
+
return null;
|
1634
|
+
}).filter(Boolean);
|
1865
1635
|
function isUsedOutsideOfHook(ref) {
|
1866
1636
|
let foundWriteExpr = false;
|
1867
1637
|
for (let i = 0; i < ref.references.length; i++) {
|
@@ -1877,10 +1647,8 @@ function scanForConstructions({
|
|
1877
1647
|
while (currentScope !== scope && currentScope != null) {
|
1878
1648
|
currentScope = currentScope.upper;
|
1879
1649
|
}
|
1880
|
-
if (
|
1881
|
-
|
1882
|
-
!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)
|
1883
|
-
) {
|
1650
|
+
if (currentScope !== scope &&
|
1651
|
+
!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)) {
|
1884
1652
|
return true;
|
1885
1653
|
}
|
1886
1654
|
}
|
@@ -1893,26 +1661,10 @@ function scanForConstructions({
|
|
1893
1661
|
}));
|
1894
1662
|
}
|
1895
1663
|
function getDependency(node) {
|
1896
|
-
if (
|
1897
|
-
(node.parent.type === 'MemberExpression' ||
|
1898
|
-
node.parent.type === 'OptionalMemberExpression') &&
|
1899
|
-
node.parent.object === node &&
|
1900
|
-
node.parent.property.name !== 'current' &&
|
1901
|
-
!node.parent.computed &&
|
1902
|
-
!(
|
1903
|
-
node.parent.parent != null &&
|
1904
|
-
(node.parent.parent.type === 'CallExpression' ||
|
1905
|
-
node.parent.parent.type === 'OptionalCallExpression') &&
|
1906
|
-
node.parent.parent.callee === node.parent
|
1907
|
-
)
|
1908
|
-
) {
|
1664
|
+
if ((node.parent.type === 'MemberExpression' || node.parent.type === 'OptionalMemberExpression') && node.parent.object === node && node.parent.property.name !== 'current' && !node.parent.computed && !(node.parent.parent != null && (node.parent.parent.type === 'CallExpression' || node.parent.parent.type === 'OptionalCallExpression') && node.parent.parent.callee === node.parent)) {
|
1909
1665
|
return getDependency(node.parent);
|
1910
1666
|
} else if (
|
1911
|
-
|
1912
|
-
node.parent &&
|
1913
|
-
node.parent.type === 'AssignmentExpression' &&
|
1914
|
-
node.parent.left === node
|
1915
|
-
) {
|
1667
|
+
node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
|
1916
1668
|
return node.object;
|
1917
1669
|
}
|
1918
1670
|
return node;
|
@@ -1938,36 +1690,30 @@ function analyzePropertyChain(node, optionalChains) {
|
|
1938
1690
|
} else if (node.type === 'MemberExpression' && !node.computed) {
|
1939
1691
|
const object = analyzePropertyChain(node.object, optionalChains);
|
1940
1692
|
const property = analyzePropertyChain(node.property, null);
|
1941
|
-
const result =
|
1693
|
+
const result = "".concat(object, ".").concat(property);
|
1942
1694
|
markNode(node, optionalChains, result);
|
1943
1695
|
return result;
|
1944
1696
|
} else if (node.type === 'OptionalMemberExpression' && !node.computed) {
|
1945
1697
|
const object = analyzePropertyChain(node.object, optionalChains);
|
1946
1698
|
const property = analyzePropertyChain(node.property, null);
|
1947
|
-
const result =
|
1699
|
+
const result = "".concat(object, ".").concat(property);
|
1948
1700
|
markNode(node, optionalChains, result);
|
1949
1701
|
return result;
|
1950
1702
|
} else if (node.type === 'ChainExpression' && !node.computed) {
|
1951
1703
|
const expression = node.expression;
|
1952
1704
|
if (expression.type === 'CallExpression') {
|
1953
|
-
throw new Error(
|
1705
|
+
throw new Error("Unsupported node type: ".concat(expression.type));
|
1954
1706
|
}
|
1955
1707
|
const object = analyzePropertyChain(expression.object, optionalChains);
|
1956
1708
|
const property = analyzePropertyChain(expression.property, null);
|
1957
|
-
const result =
|
1709
|
+
const result = "".concat(object, ".").concat(property);
|
1958
1710
|
markNode(expression, optionalChains, result);
|
1959
1711
|
return result;
|
1960
1712
|
}
|
1961
|
-
throw new Error(
|
1713
|
+
throw new Error("Unsupported node type: ".concat(node.type));
|
1962
1714
|
}
|
1963
1715
|
function getNodeWithoutReactNamespace(node) {
|
1964
|
-
if (
|
1965
|
-
node.type === 'MemberExpression' &&
|
1966
|
-
node.object.type === 'Identifier' &&
|
1967
|
-
node.object.name === 'React' &&
|
1968
|
-
node.property.type === 'Identifier' &&
|
1969
|
-
!node.computed
|
1970
|
-
) {
|
1716
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'React' && node.property.type === 'Identifier' && !node.computed) {
|
1971
1717
|
return node.property;
|
1972
1718
|
}
|
1973
1719
|
return node;
|
@@ -2046,28 +1792,17 @@ function joinEnglish(arr) {
|
|
2046
1792
|
return s;
|
2047
1793
|
}
|
2048
1794
|
function isNodeLike(val) {
|
2049
|
-
return (
|
2050
|
-
typeof val === 'object' &&
|
2051
|
-
val != null &&
|
2052
|
-
!Array.isArray(val) &&
|
2053
|
-
typeof val.type === 'string'
|
2054
|
-
);
|
1795
|
+
return typeof val === 'object' && val != null && !Array.isArray(val) && typeof val.type === 'string';
|
2055
1796
|
}
|
2056
1797
|
function isSameIdentifier(a, b) {
|
2057
|
-
return (
|
2058
|
-
(a.type === 'Identifier' || a.type === 'JSXIdentifier') &&
|
2059
|
-
a.type === b.type &&
|
2060
|
-
a.name === b.name &&
|
2061
|
-
a.range[0] === b.range[0] &&
|
2062
|
-
a.range[1] === b.range[1]
|
2063
|
-
);
|
1798
|
+
return (a.type === 'Identifier' || a.type === 'JSXIdentifier') && a.type === b.type && a.name === b.name && a.range[0] === b.range[0] && a.range[1] === b.range[1];
|
2064
1799
|
}
|
2065
1800
|
function isAncestorNodeOf(a, b) {
|
2066
1801
|
return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
|
2067
1802
|
}
|
2068
1803
|
|
2069
1804
|
const Components = require('eslint-plugin-react/lib/util/Components');
|
2070
|
-
var
|
1805
|
+
var reactHookUseRef = {
|
2071
1806
|
meta: {
|
2072
1807
|
docs: {
|
2073
1808
|
description: 'Ensure naming of useRef hook value.',
|
@@ -2075,12 +1810,14 @@ var hookUseRef = {
|
|
2075
1810
|
},
|
2076
1811
|
schema: [],
|
2077
1812
|
type: 'suggestion',
|
2078
|
-
hasSuggestions: true
|
1813
|
+
hasSuggestions: true,
|
1814
|
+
messages: {
|
1815
|
+
useRefName: 'useRef call is not end with "Ref"'
|
1816
|
+
}
|
2079
1817
|
},
|
2080
1818
|
create: Components.detect((context, component, util) => ({
|
2081
1819
|
CallExpression(node) {
|
2082
|
-
const isImmediateReturn =
|
2083
|
-
node.parent && node.parent.type === 'ReturnStatement';
|
1820
|
+
const isImmediateReturn = node.parent && node.parent.type === 'ReturnStatement';
|
2084
1821
|
if (isImmediateReturn || !util.isReactHookCall(node, ['useRef'])) {
|
2085
1822
|
return;
|
2086
1823
|
}
|
@@ -2091,7 +1828,7 @@ var hookUseRef = {
|
|
2091
1828
|
if (!variable.endsWith('Ref')) {
|
2092
1829
|
context.report({
|
2093
1830
|
node: node,
|
2094
|
-
|
1831
|
+
messageId: 'useRefName'
|
2095
1832
|
});
|
2096
1833
|
}
|
2097
1834
|
}
|
@@ -2100,54 +1837,39 @@ var hookUseRef = {
|
|
2100
1837
|
|
2101
1838
|
function* updateImportStatement(context, fixer, key) {
|
2102
1839
|
const sourceCode = context.sourceCode;
|
2103
|
-
const importNode = sourceCode.ast.body.find(
|
2104
|
-
node => node.type === 'ImportDeclaration' && node.source.value === 'react'
|
2105
|
-
);
|
1840
|
+
const importNode = sourceCode.ast.body.find(node => node.type === 'ImportDeclaration' && node.source.value === 'react');
|
2106
1841
|
if (!importNode) {
|
2107
|
-
yield fixer.insertTextBefore(
|
2108
|
-
sourceCode.ast.body[0],
|
2109
|
-
`import { ${key} } from 'react';\n`
|
2110
|
-
);
|
1842
|
+
yield fixer.insertTextBefore(sourceCode.ast.body[0], "import { ".concat(key, " } from 'react';\n"));
|
2111
1843
|
return;
|
2112
1844
|
}
|
2113
|
-
if (
|
2114
|
-
importNode.specifiers.
|
2115
|
-
importNode.specifiers[0].type === 'ImportDefaultSpecifier'
|
2116
|
-
) {
|
2117
|
-
yield fixer.insertTextAfter(importNode.specifiers[0], `, { ${key} }`);
|
1845
|
+
if (importNode.specifiers.length === 1 && importNode.specifiers[0].type === 'ImportDefaultSpecifier') {
|
1846
|
+
yield fixer.insertTextAfter(importNode.specifiers[0], ", { ".concat(key, " }"));
|
2118
1847
|
return;
|
2119
1848
|
}
|
2120
|
-
const alreadyImportedKeys = importNode.specifiers
|
2121
|
-
.filter(specifier => specifier.type === 'ImportSpecifier')
|
2122
|
-
.map(specifier => specifier.imported.name);
|
1849
|
+
const alreadyImportedKeys = importNode.specifiers.filter(specifier => specifier.type === 'ImportSpecifier').map(specifier => specifier.imported.name);
|
2123
1850
|
if (alreadyImportedKeys.includes(key)) {
|
2124
1851
|
return;
|
2125
1852
|
}
|
2126
|
-
yield fixer.insertTextAfter([...importNode.specifiers].pop(),
|
1853
|
+
yield fixer.insertTextAfter([...importNode.specifiers].pop(), ", ".concat(key));
|
2127
1854
|
}
|
2128
|
-
var
|
1855
|
+
var reactPreferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
|
2129
1856
|
defaultOptions: [],
|
2130
1857
|
meta: {
|
2131
1858
|
type: 'layout',
|
2132
1859
|
fixable: 'code',
|
2133
1860
|
docs: {
|
2134
|
-
description:
|
2135
|
-
'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
|
1861
|
+
description: 'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
|
2136
1862
|
recommended: 'recommended'
|
2137
1863
|
},
|
2138
1864
|
messages: {
|
2139
|
-
illegalReactPropertyAccess:
|
2140
|
-
'Illegal React property access: {{name}}. Use named import instead.'
|
1865
|
+
illegalReactPropertyAccess: 'Illegal React property access: {{name}}. Use named import instead.'
|
2141
1866
|
},
|
2142
1867
|
schema: []
|
2143
1868
|
},
|
2144
1869
|
create(context) {
|
2145
1870
|
return {
|
2146
1871
|
TSQualifiedName(node) {
|
2147
|
-
if (
|
2148
|
-
('name' in node.left && node.left.name !== 'React') ||
|
2149
|
-
('name' in node.right && node.right.name.endsWith('Event'))
|
2150
|
-
) {
|
1872
|
+
if ('name' in node.left && node.left.name !== 'React' || 'name' in node.right && node.right.name.endsWith('Event')) {
|
2151
1873
|
return;
|
2152
1874
|
}
|
2153
1875
|
context.report({
|
@@ -2182,7 +1904,7 @@ var preferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
2182
1904
|
}
|
2183
1905
|
});
|
2184
1906
|
|
2185
|
-
var
|
1907
|
+
var reactPreferSxProp = {
|
2186
1908
|
meta: {
|
2187
1909
|
docs: {
|
2188
1910
|
description: 'Prefer using sx prop instead of inline styles',
|
@@ -2190,36 +1912,29 @@ var preferSxProp = {
|
|
2190
1912
|
recommended: true
|
2191
1913
|
},
|
2192
1914
|
messages: {
|
2193
|
-
preferSxProp:
|
2194
|
-
'Avoid using inline styles, use sx prop or tss-react or styled-component instead'
|
1915
|
+
preferSxProp: 'Avoid using inline styles, use sx prop or tss-react or styled-component instead'
|
2195
1916
|
},
|
2196
|
-
schema: [
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2201
|
-
|
2202
|
-
|
2203
|
-
|
1917
|
+
schema: [{
|
1918
|
+
type: 'object',
|
1919
|
+
properties: {
|
1920
|
+
allowedFor: {
|
1921
|
+
type: 'array',
|
1922
|
+
uniqueItems: true,
|
1923
|
+
items: {
|
1924
|
+
type: 'string'
|
2204
1925
|
}
|
2205
1926
|
}
|
2206
1927
|
}
|
2207
|
-
]
|
1928
|
+
}]
|
2208
1929
|
},
|
2209
1930
|
create(context) {
|
2210
1931
|
const configuration = context.options[0] || {};
|
2211
1932
|
const allowedFor = configuration.allowedFor || [];
|
2212
1933
|
function checkComponent(node) {
|
2213
1934
|
const parentName = node.parent.name;
|
2214
|
-
const tag =
|
2215
|
-
parentName.name ||
|
2216
|
-
`${parentName.object.name}.${parentName.property.name}`;
|
1935
|
+
const tag = parentName.name || "".concat(parentName.object.name, ".").concat(parentName.property.name);
|
2217
1936
|
const componentName = parentName.name || parentName.property.name;
|
2218
|
-
if (
|
2219
|
-
componentName &&
|
2220
|
-
typeof componentName[0] === 'string' &&
|
2221
|
-
componentName[0] !== componentName[0].toUpperCase()
|
2222
|
-
) {
|
1937
|
+
if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) {
|
2223
1938
|
return;
|
2224
1939
|
}
|
2225
1940
|
if (allowedFor.includes(tag)) {
|
@@ -2235,9 +1950,7 @@ var preferSxProp = {
|
|
2235
1950
|
}
|
2236
1951
|
function checkDOMNodes(node) {
|
2237
1952
|
const tag = node.parent.name.name;
|
2238
|
-
if (
|
2239
|
-
!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())
|
2240
|
-
) {
|
1953
|
+
if (!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())) {
|
2241
1954
|
return;
|
2242
1955
|
}
|
2243
1956
|
if (allowedFor.includes(tag)) {
|
@@ -2277,25 +1990,24 @@ function getBasicIdentifier(node) {
|
|
2277
1990
|
}
|
2278
1991
|
function getBaseIdentifier(node) {
|
2279
1992
|
switch (node.type) {
|
2280
|
-
case 'Identifier':
|
2281
|
-
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
|
2287
|
-
|
2288
|
-
|
1993
|
+
case 'Identifier':
|
1994
|
+
{
|
1995
|
+
return node;
|
1996
|
+
}
|
1997
|
+
case 'CallExpression':
|
1998
|
+
{
|
1999
|
+
return getBaseIdentifier(node.callee);
|
2000
|
+
}
|
2001
|
+
case 'MemberExpression':
|
2002
|
+
{
|
2003
|
+
return getBaseIdentifier(node.object);
|
2004
|
+
}
|
2289
2005
|
}
|
2290
2006
|
return null;
|
2291
2007
|
}
|
2292
2008
|
function getStyesObj(node) {
|
2293
2009
|
const isMakeStyles = node.callee.name === 'makeStyles';
|
2294
|
-
const isModernApi =
|
2295
|
-
node.callee.type === 'MemberExpression' &&
|
2296
|
-
node.callee.property.name === 'create' &&
|
2297
|
-
getBaseIdentifier(node.callee.object) &&
|
2298
|
-
getBaseIdentifier(node.callee.object).name === 'tss';
|
2010
|
+
const isModernApi = node.callee.type === 'MemberExpression' && node.callee.property.name === 'create' && getBaseIdentifier(node.callee.object) && getBaseIdentifier(node.callee.object).name === 'tss';
|
2299
2011
|
if (!isMakeStyles && !isModernApi) {
|
2300
2012
|
return;
|
2301
2013
|
}
|
@@ -2315,35 +2027,51 @@ function getStyesObj(node) {
|
|
2315
2027
|
return styles;
|
2316
2028
|
case 'ArrowFunctionExpression':
|
2317
2029
|
{
|
2318
|
-
const {
|
2030
|
+
const {
|
2031
|
+
body
|
2032
|
+
} = styles;
|
2319
2033
|
switch (body.type) {
|
2320
2034
|
case 'ObjectExpression':
|
2321
2035
|
return body;
|
2322
|
-
case 'BlockStatement':
|
2323
|
-
|
2324
|
-
|
2325
|
-
|
2326
|
-
bodyNode.type === 'ReturnStatement' &&
|
2327
|
-
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
}
|
2332
|
-
return stylesObj;
|
2333
|
-
}
|
2036
|
+
case 'BlockStatement':
|
2037
|
+
{
|
2038
|
+
let stylesObj;
|
2039
|
+
body.body.forEach(bodyNode => {
|
2040
|
+
if (bodyNode.type === 'ReturnStatement' && bodyNode.argument.type === 'ObjectExpression') {
|
2041
|
+
stylesObj = bodyNode.argument;
|
2042
|
+
}
|
2043
|
+
});
|
2044
|
+
return stylesObj;
|
2045
|
+
}
|
2334
2046
|
}
|
2335
2047
|
}
|
2336
2048
|
break;
|
2337
2049
|
}
|
2338
2050
|
}
|
2051
|
+
function loopStylesObj(node, callback) {
|
2052
|
+
if (node && node.type === 'ObjectExpression') {
|
2053
|
+
node.properties.forEach(property => {
|
2054
|
+
if (property.type === 'Property' && property.value) {
|
2055
|
+
if (property.value.type === 'ObjectExpression') {
|
2056
|
+
loopStylesObj(property.value, callback);
|
2057
|
+
} else {
|
2058
|
+
callback(property.value);
|
2059
|
+
}
|
2060
|
+
}
|
2061
|
+
});
|
2062
|
+
}
|
2063
|
+
}
|
2339
2064
|
|
2340
|
-
var
|
2065
|
+
var tssClassNaming = {
|
2341
2066
|
meta: {
|
2342
2067
|
type: 'problem',
|
2343
2068
|
docs: {
|
2344
2069
|
description: 'Enforce camelCase class names in TSS',
|
2345
2070
|
category: 'Best Practices',
|
2346
2071
|
recommended: true
|
2072
|
+
},
|
2073
|
+
messages: {
|
2074
|
+
camelCase: 'Class `{{ className }}` must be camelCase in TSS.'
|
2347
2075
|
}
|
2348
2076
|
},
|
2349
2077
|
create: function rule(context) {
|
@@ -2357,17 +2085,17 @@ var classNaming = {
|
|
2357
2085
|
if (property.computed) {
|
2358
2086
|
return;
|
2359
2087
|
}
|
2360
|
-
if (
|
2361
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
2362
|
-
property.type === 'SpreadElement'
|
2363
|
-
) {
|
2088
|
+
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
|
2364
2089
|
return;
|
2365
2090
|
}
|
2366
2091
|
const className = property.key.value || property.key.name;
|
2367
2092
|
if (!eslintUtils.isCamelCase(className)) {
|
2368
2093
|
context.report({
|
2369
2094
|
node: property,
|
2370
|
-
|
2095
|
+
messageId: 'camelCase',
|
2096
|
+
data: {
|
2097
|
+
className
|
2098
|
+
}
|
2371
2099
|
});
|
2372
2100
|
}
|
2373
2101
|
});
|
@@ -2376,20 +2104,201 @@ var classNaming = {
|
|
2376
2104
|
}
|
2377
2105
|
};
|
2378
2106
|
|
2379
|
-
var
|
2107
|
+
var colors = {
|
2108
|
+
aliceblue: [240, 248, 255],
|
2109
|
+
antiquewhite: [250, 235, 215],
|
2110
|
+
aqua: [0, 255, 255],
|
2111
|
+
aquamarine: [127, 255, 212],
|
2112
|
+
azure: [240, 255, 255],
|
2113
|
+
beige: [245, 245, 220],
|
2114
|
+
bisque: [255, 228, 196],
|
2115
|
+
black: [0, 0, 0],
|
2116
|
+
blanchedalmond: [255, 235, 205],
|
2117
|
+
blue: [0, 0, 255],
|
2118
|
+
blueviolet: [138, 43, 226],
|
2119
|
+
brown: [165, 42, 42],
|
2120
|
+
burlywood: [222, 184, 135],
|
2121
|
+
cadetblue: [95, 158, 160],
|
2122
|
+
chartreuse: [127, 255, 0],
|
2123
|
+
chocolate: [210, 105, 30],
|
2124
|
+
coral: [255, 127, 80],
|
2125
|
+
cornflowerblue: [100, 149, 237],
|
2126
|
+
cornsilk: [255, 248, 220],
|
2127
|
+
crimson: [220, 20, 60],
|
2128
|
+
cyan: [0, 255, 255],
|
2129
|
+
darkblue: [0, 0, 139],
|
2130
|
+
darkcyan: [0, 139, 139],
|
2131
|
+
darkgoldenrod: [184, 134, 11],
|
2132
|
+
darkgray: [169, 169, 169],
|
2133
|
+
darkgreen: [0, 100, 0],
|
2134
|
+
darkgrey: [169, 169, 169],
|
2135
|
+
darkkhaki: [189, 183, 107],
|
2136
|
+
darkmagenta: [139, 0, 139],
|
2137
|
+
darkolivegreen: [85, 107, 47],
|
2138
|
+
darkorange: [255, 140, 0],
|
2139
|
+
darkorchid: [153, 50, 204],
|
2140
|
+
darkred: [139, 0, 0],
|
2141
|
+
darksalmon: [233, 150, 122],
|
2142
|
+
darkseagreen: [143, 188, 143],
|
2143
|
+
darkslateblue: [72, 61, 139],
|
2144
|
+
darkslategray: [47, 79, 79],
|
2145
|
+
darkslategrey: [47, 79, 79],
|
2146
|
+
darkturquoise: [0, 206, 209],
|
2147
|
+
darkviolet: [148, 0, 211],
|
2148
|
+
deeppink: [255, 20, 147],
|
2149
|
+
deepskyblue: [0, 191, 255],
|
2150
|
+
dimgray: [105, 105, 105],
|
2151
|
+
dimgrey: [105, 105, 105],
|
2152
|
+
dodgerblue: [30, 144, 255],
|
2153
|
+
firebrick: [178, 34, 34],
|
2154
|
+
floralwhite: [255, 250, 240],
|
2155
|
+
forestgreen: [34, 139, 34],
|
2156
|
+
fuchsia: [255, 0, 255],
|
2157
|
+
gainsboro: [220, 220, 220],
|
2158
|
+
ghostwhite: [248, 248, 255],
|
2159
|
+
gold: [255, 215, 0],
|
2160
|
+
goldenrod: [218, 165, 32],
|
2161
|
+
gray: [128, 128, 128],
|
2162
|
+
green: [0, 128, 0],
|
2163
|
+
greenyellow: [173, 255, 47],
|
2164
|
+
grey: [128, 128, 128],
|
2165
|
+
honeydew: [240, 255, 240],
|
2166
|
+
hotpink: [255, 105, 180],
|
2167
|
+
indianred: [205, 92, 92],
|
2168
|
+
indigo: [75, 0, 130],
|
2169
|
+
ivory: [255, 255, 240],
|
2170
|
+
khaki: [240, 230, 140],
|
2171
|
+
lavender: [230, 230, 250],
|
2172
|
+
lavenderblush: [255, 240, 245],
|
2173
|
+
lawngreen: [124, 252, 0],
|
2174
|
+
lemonchiffon: [255, 250, 205],
|
2175
|
+
lightblue: [173, 216, 230],
|
2176
|
+
lightcoral: [240, 128, 128],
|
2177
|
+
lightcyan: [224, 255, 255],
|
2178
|
+
lightgoldenrodyellow: [250, 250, 210],
|
2179
|
+
lightgray: [211, 211, 211],
|
2180
|
+
lightgreen: [144, 238, 144],
|
2181
|
+
lightgrey: [211, 211, 211],
|
2182
|
+
lightpink: [255, 182, 193],
|
2183
|
+
lightsalmon: [255, 160, 122],
|
2184
|
+
lightseagreen: [32, 178, 170],
|
2185
|
+
lightskyblue: [135, 206, 250],
|
2186
|
+
lightslategray: [119, 136, 153],
|
2187
|
+
lightslategrey: [119, 136, 153],
|
2188
|
+
lightsteelblue: [176, 196, 222],
|
2189
|
+
lightyellow: [255, 255, 224],
|
2190
|
+
lime: [0, 255, 0],
|
2191
|
+
limegreen: [50, 205, 50],
|
2192
|
+
linen: [250, 240, 230],
|
2193
|
+
magenta: [255, 0, 255],
|
2194
|
+
maroon: [128, 0, 0],
|
2195
|
+
mediumaquamarine: [102, 205, 170],
|
2196
|
+
mediumblue: [0, 0, 205],
|
2197
|
+
mediumorchid: [186, 85, 211],
|
2198
|
+
mediumpurple: [147, 112, 219],
|
2199
|
+
mediumseagreen: [60, 179, 113],
|
2200
|
+
mediumslateblue: [123, 104, 238],
|
2201
|
+
mediumspringgreen: [0, 250, 154],
|
2202
|
+
mediumturquoise: [72, 209, 204],
|
2203
|
+
mediumvioletred: [199, 21, 133],
|
2204
|
+
midnightblue: [25, 25, 112],
|
2205
|
+
mintcream: [245, 255, 250],
|
2206
|
+
mistyrose: [255, 228, 225],
|
2207
|
+
moccasin: [255, 228, 181],
|
2208
|
+
navajowhite: [255, 222, 173],
|
2209
|
+
navy: [0, 0, 128],
|
2210
|
+
oldlace: [253, 245, 230],
|
2211
|
+
olive: [128, 128, 0],
|
2212
|
+
olivedrab: [107, 142, 35],
|
2213
|
+
orange: [255, 165, 0],
|
2214
|
+
orangered: [255, 69, 0],
|
2215
|
+
orchid: [218, 112, 214],
|
2216
|
+
palegoldenrod: [238, 232, 170],
|
2217
|
+
palegreen: [152, 251, 152],
|
2218
|
+
paleturquoise: [175, 238, 238],
|
2219
|
+
palevioletred: [219, 112, 147],
|
2220
|
+
papayawhip: [255, 239, 213],
|
2221
|
+
peachpuff: [255, 218, 185],
|
2222
|
+
peru: [205, 133, 63],
|
2223
|
+
pink: [255, 192, 203],
|
2224
|
+
plum: [221, 160, 221],
|
2225
|
+
powderblue: [176, 224, 230],
|
2226
|
+
purple: [128, 0, 128],
|
2227
|
+
rebeccapurple: [102, 51, 153],
|
2228
|
+
red: [255, 0, 0],
|
2229
|
+
rosybrown: [188, 143, 143],
|
2230
|
+
royalblue: [65, 105, 225],
|
2231
|
+
saddlebrown: [139, 69, 19],
|
2232
|
+
salmon: [250, 128, 114],
|
2233
|
+
sandybrown: [244, 164, 96],
|
2234
|
+
seagreen: [46, 139, 87],
|
2235
|
+
seashell: [255, 245, 238],
|
2236
|
+
sienna: [160, 82, 45],
|
2237
|
+
silver: [192, 192, 192],
|
2238
|
+
skyblue: [135, 206, 235],
|
2239
|
+
slateblue: [106, 90, 205],
|
2240
|
+
slategray: [112, 128, 144],
|
2241
|
+
slategrey: [112, 128, 144],
|
2242
|
+
snow: [255, 250, 250],
|
2243
|
+
springgreen: [0, 255, 127],
|
2244
|
+
steelblue: [70, 130, 180],
|
2245
|
+
tan: [210, 180, 140],
|
2246
|
+
teal: [0, 128, 128],
|
2247
|
+
thistle: [216, 191, 216],
|
2248
|
+
tomato: [255, 99, 71],
|
2249
|
+
turquoise: [64, 224, 208],
|
2250
|
+
violet: [238, 130, 238],
|
2251
|
+
wheat: [245, 222, 179],
|
2252
|
+
white: [255, 255, 255],
|
2253
|
+
whitesmoke: [245, 245, 245],
|
2254
|
+
yellow: [255, 255, 0],
|
2255
|
+
yellowgreen: [154, 205, 50]
|
2256
|
+
};
|
2257
|
+
|
2258
|
+
var tssNoColorName = {
|
2380
2259
|
meta: {
|
2381
|
-
type: '
|
2260
|
+
type: 'suggestion',
|
2382
2261
|
docs: {
|
2383
|
-
description:
|
2384
|
-
'Enforce the use of color variables instead of color codes within TSS',
|
2262
|
+
description: 'Enforce the use of color variables instead of color name within TSS',
|
2385
2263
|
recommended: true
|
2264
|
+
},
|
2265
|
+
messages: {
|
2266
|
+
disallowColorName: 'Disallowed color name. Use color from `@mui/material/colors` or `theme.palette`.'
|
2386
2267
|
}
|
2387
2268
|
},
|
2388
2269
|
create: function (context) {
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2270
|
+
return {
|
2271
|
+
CallExpression(node) {
|
2272
|
+
const stylesObj = getStyesObj(node);
|
2273
|
+
if (!stylesObj) {
|
2274
|
+
return;
|
2275
|
+
}
|
2276
|
+
function checkColorLiteral(value) {
|
2277
|
+
if (value.type === 'Literal' && typeof value.value === 'string' && Object.keys(colors).includes(value.value.toLowerCase())) {
|
2278
|
+
context.report({
|
2279
|
+
node: value,
|
2280
|
+
messageId: 'disallowColorName'
|
2281
|
+
});
|
2282
|
+
}
|
2283
|
+
}
|
2284
|
+
loopStylesObj(stylesObj, checkColorLiteral);
|
2285
|
+
}
|
2286
|
+
};
|
2287
|
+
}
|
2288
|
+
};
|
2289
|
+
|
2290
|
+
var tssNoColorValue = {
|
2291
|
+
meta: {
|
2292
|
+
type: 'problem',
|
2293
|
+
docs: {
|
2294
|
+
description: 'Enforce the use of color variables instead of color codes within TSS',
|
2295
|
+
recommended: true
|
2296
|
+
},
|
2297
|
+
messages: {
|
2298
|
+
preferMuiColor: 'Prefer use color from `@mui/material/colors` or `theme.palette`.'
|
2392
2299
|
}
|
2300
|
+
},
|
2301
|
+
create: function (context) {
|
2393
2302
|
return {
|
2394
2303
|
CallExpression(node) {
|
2395
2304
|
const stylesObj = getStyesObj(node);
|
@@ -2398,43 +2307,31 @@ var noColorValue = {
|
|
2398
2307
|
}
|
2399
2308
|
function checkColorLiteral(value) {
|
2400
2309
|
if (value.type === 'Literal' && typeof value.value === 'string') {
|
2401
|
-
const colorCodePattern =
|
2402
|
-
|
2403
|
-
const isColorCode = colorCodePattern.test(value.value);
|
2404
|
-
if (isColorCode) {
|
2310
|
+
const colorCodePattern = /#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgb\?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}(?:\s*,\s*\d*(?:\.\d+)?)?\s*\)/g;
|
2311
|
+
if (colorCodePattern.test(value.value)) {
|
2405
2312
|
context.report({
|
2406
2313
|
node: value,
|
2407
|
-
|
2314
|
+
messageId: 'preferMuiColor'
|
2408
2315
|
});
|
2409
2316
|
}
|
2410
2317
|
}
|
2411
2318
|
}
|
2412
|
-
|
2413
|
-
if (obj && obj.type === 'ObjectExpression') {
|
2414
|
-
obj.properties.forEach(property => {
|
2415
|
-
if (property.type === 'Property' && property.value) {
|
2416
|
-
if (property.value.type === 'ObjectExpression') {
|
2417
|
-
loopStylesObj(property.value);
|
2418
|
-
} else {
|
2419
|
-
checkColorLiteral(property.value);
|
2420
|
-
}
|
2421
|
-
}
|
2422
|
-
});
|
2423
|
-
}
|
2424
|
-
}
|
2425
|
-
loopStylesObj(stylesObj);
|
2319
|
+
loopStylesObj(stylesObj, checkColorLiteral);
|
2426
2320
|
}
|
2427
2321
|
};
|
2428
2322
|
}
|
2429
2323
|
};
|
2430
2324
|
|
2431
|
-
var
|
2325
|
+
var tssUnusedClasses = {
|
2432
2326
|
meta: {
|
2433
2327
|
type: 'suggestion',
|
2434
2328
|
docs: {
|
2435
2329
|
description: 'Disallow unused classes in tss',
|
2436
2330
|
category: 'Best Practices',
|
2437
2331
|
recommended: true
|
2332
|
+
},
|
2333
|
+
messages: {
|
2334
|
+
unusedClass: 'Class `{{ className }}` is unused in JSX'
|
2438
2335
|
}
|
2439
2336
|
},
|
2440
2337
|
create: function rule(context) {
|
@@ -2450,20 +2347,14 @@ var unusedClasses = {
|
|
2450
2347
|
if (property.computed) {
|
2451
2348
|
return;
|
2452
2349
|
}
|
2453
|
-
if (
|
2454
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
2455
|
-
property.type === 'SpreadElement'
|
2456
|
-
) {
|
2350
|
+
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
|
2457
2351
|
return;
|
2458
2352
|
}
|
2459
2353
|
definedClasses[property.key.value || property.key.name] = property;
|
2460
2354
|
});
|
2461
2355
|
},
|
2462
2356
|
MemberExpression(node) {
|
2463
|
-
if (
|
2464
|
-
node.object.type === 'Identifier' &&
|
2465
|
-
node.object.name === 'classes'
|
2466
|
-
) {
|
2357
|
+
if (node.object.type === 'Identifier' && node.object.name === 'classes') {
|
2467
2358
|
const whichClass = getBasicIdentifier(node.property);
|
2468
2359
|
if (whichClass) {
|
2469
2360
|
usedClasses[whichClass] = true;
|
@@ -2477,14 +2368,15 @@ var unusedClasses = {
|
|
2477
2368
|
if (classIdentifier !== 'classes') {
|
2478
2369
|
return;
|
2479
2370
|
}
|
2480
|
-
const {
|
2371
|
+
const {
|
2372
|
+
parent
|
2373
|
+
} = node;
|
2481
2374
|
if (parent.type !== 'MemberExpression') {
|
2482
2375
|
return;
|
2483
2376
|
}
|
2484
2377
|
if (
|
2485
|
-
|
2486
|
-
|
2487
|
-
) {
|
2378
|
+
node.object.object &&
|
2379
|
+
node.object.object.type !== 'ThisExpression') {
|
2488
2380
|
return;
|
2489
2381
|
}
|
2490
2382
|
const propsIdentifier = getBasicIdentifier(parent.object);
|
@@ -2507,7 +2399,10 @@ var unusedClasses = {
|
|
2507
2399
|
if (!usedClasses[definedClassKey]) {
|
2508
2400
|
context.report({
|
2509
2401
|
node: definedClasses[definedClassKey],
|
2510
|
-
|
2402
|
+
messageId: 'unusedClass',
|
2403
|
+
data: {
|
2404
|
+
className: definedClassKey
|
2405
|
+
}
|
2511
2406
|
});
|
2512
2407
|
}
|
2513
2408
|
});
|
@@ -2516,177 +2411,26 @@ var unusedClasses = {
|
|
2516
2411
|
}
|
2517
2412
|
};
|
2518
2413
|
|
2519
|
-
var noAsyncArrayMethods = {
|
2520
|
-
meta: {
|
2521
|
-
docs: {
|
2522
|
-
description:
|
2523
|
-
'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
|
2524
|
-
category: 'Array',
|
2525
|
-
recommended: true
|
2526
|
-
},
|
2527
|
-
fixable: undefined,
|
2528
|
-
schema: []
|
2529
|
-
},
|
2530
|
-
create: function (context) {
|
2531
|
-
return {
|
2532
|
-
ExpressionStatement: function (node) {
|
2533
|
-
const notAllowedArrayMethods = [
|
2534
|
-
'forEach',
|
2535
|
-
'filter',
|
2536
|
-
'some',
|
2537
|
-
'every',
|
2538
|
-
'map',
|
2539
|
-
'reduce',
|
2540
|
-
'reduceRight',
|
2541
|
-
'flatMap',
|
2542
|
-
'find',
|
2543
|
-
'findIndex',
|
2544
|
-
'findLast',
|
2545
|
-
'findLastIndex'
|
2546
|
-
];
|
2547
|
-
const { callee } = node.expression;
|
2548
|
-
if (!callee || !callee.property || !callee.property.name) {
|
2549
|
-
return;
|
2550
|
-
}
|
2551
|
-
if (notAllowedArrayMethods.includes(callee.property.name)) {
|
2552
|
-
const functionArguments = node.expression.arguments.find(n => {
|
2553
|
-
return ['ArrowFunctionExpression', 'FunctionExpression'].includes(
|
2554
|
-
n.type
|
2555
|
-
);
|
2556
|
-
});
|
2557
|
-
if (functionArguments && functionArguments.async) {
|
2558
|
-
context.report({
|
2559
|
-
node,
|
2560
|
-
message: `No async function in method '${callee.property.name}'`
|
2561
|
-
});
|
2562
|
-
}
|
2563
|
-
}
|
2564
|
-
}
|
2565
|
-
};
|
2566
|
-
}
|
2567
|
-
};
|
2568
|
-
|
2569
|
-
var noThenCatchFinally = {
|
2570
|
-
meta: {
|
2571
|
-
type: 'suggestion',
|
2572
|
-
docs: {
|
2573
|
-
description:
|
2574
|
-
'Disallow the use of then()/catch()/finally() when invoking specific functions.'
|
2575
|
-
},
|
2576
|
-
schema: [
|
2577
|
-
{
|
2578
|
-
type: 'object',
|
2579
|
-
properties: {
|
2580
|
-
restrictedFunctions: {
|
2581
|
-
type: 'array',
|
2582
|
-
uniqueItems: true,
|
2583
|
-
items: { type: 'string' }
|
2584
|
-
}
|
2585
|
-
}
|
2586
|
-
}
|
2587
|
-
],
|
2588
|
-
messages: {
|
2589
|
-
forbiddenThenCatchFinally: `then()/catch()/finally() is forbidden when invoke {{ name }}().`
|
2590
|
-
}
|
2591
|
-
},
|
2592
|
-
create(context) {
|
2593
|
-
const configuration = context.options[0] || {};
|
2594
|
-
const restrictedFunctions = configuration.restrictedFunctions || [];
|
2595
|
-
function isTopLevelScoped() {
|
2596
|
-
return context.getScope().block.type === 'Program';
|
2597
|
-
}
|
2598
|
-
function isThenCatchFinally(node) {
|
2599
|
-
return (
|
2600
|
-
node.property &&
|
2601
|
-
(node.property.name === 'then' ||
|
2602
|
-
node.property.name === 'catch' ||
|
2603
|
-
node.property.name === 'finally')
|
2604
|
-
);
|
2605
|
-
}
|
2606
|
-
return {
|
2607
|
-
'CallExpression > MemberExpression.callee'(node) {
|
2608
|
-
if (isTopLevelScoped()) {
|
2609
|
-
return;
|
2610
|
-
}
|
2611
|
-
if (!isThenCatchFinally(node)) {
|
2612
|
-
return;
|
2613
|
-
}
|
2614
|
-
const callExpression = node.object;
|
2615
|
-
if (
|
2616
|
-
callExpression.type === 'CallExpression' &&
|
2617
|
-
callExpression.callee.type === 'Identifier' &&
|
2618
|
-
restrictedFunctions.includes(callExpression.callee.name)
|
2619
|
-
) {
|
2620
|
-
context.report({
|
2621
|
-
node: node.property,
|
2622
|
-
messageId: 'forbiddenThenCatchFinally',
|
2623
|
-
data: {
|
2624
|
-
name: callExpression.callee.name
|
2625
|
-
}
|
2626
|
-
});
|
2627
|
-
}
|
2628
|
-
}
|
2629
|
-
};
|
2630
|
-
}
|
2631
|
-
};
|
2632
|
-
|
2633
|
-
var noUnnecessaryTemplateLiterals = {
|
2634
|
-
meta: {
|
2635
|
-
type: 'problem',
|
2636
|
-
docs: {
|
2637
|
-
description: 'Check if a template string contains only one ${}',
|
2638
|
-
recommended: true
|
2639
|
-
},
|
2640
|
-
fixable: 'code',
|
2641
|
-
schema: [],
|
2642
|
-
messages: {
|
2643
|
-
unnecessaryTemplateString:
|
2644
|
-
'Unnecessary template string with only one ${}.'
|
2645
|
-
}
|
2646
|
-
},
|
2647
|
-
create(context) {
|
2648
|
-
return {
|
2649
|
-
TemplateLiteral(node) {
|
2650
|
-
const code = context.sourceCode.getText(node);
|
2651
|
-
if (
|
2652
|
-
code.startsWith('`${') &&
|
2653
|
-
code.endsWith('}`') &&
|
2654
|
-
code.split('${').length === 2
|
2655
|
-
) {
|
2656
|
-
context.report({
|
2657
|
-
node,
|
2658
|
-
messageId: 'unnecessaryTemplateString',
|
2659
|
-
fix(fixer) {
|
2660
|
-
return fixer.replaceText(
|
2661
|
-
node,
|
2662
|
-
code.substring(3, code.length - 2)
|
2663
|
-
);
|
2664
|
-
}
|
2665
|
-
});
|
2666
|
-
}
|
2667
|
-
}
|
2668
|
-
};
|
2669
|
-
}
|
2670
|
-
};
|
2671
|
-
|
2672
2414
|
var ruleFiles = /*#__PURE__*/Object.freeze({
|
2673
2415
|
__proto__: null,
|
2674
|
-
|
2675
|
-
rules_import_monorepo:
|
2676
|
-
rules_intl_id_missing:
|
2677
|
-
rules_intl_id_prefix:
|
2678
|
-
rules_intl_id_unused:
|
2679
|
-
rules_intl_no_default:
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2684
|
-
|
2685
|
-
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2416
|
+
rules_enforce_mui_icon_alias: enforceMuiIconAlias,
|
2417
|
+
rules_import_monorepo: importMonorepo,
|
2418
|
+
rules_intl_id_missing: intlIdMissing,
|
2419
|
+
rules_intl_id_prefix: intlIdPrefix,
|
2420
|
+
rules_intl_id_unused: intlIdUnused,
|
2421
|
+
rules_intl_no_default: intlNoDefault,
|
2422
|
+
rules_no_async_array_methods: noAsyncArrayMethods,
|
2423
|
+
rules_no_import_css: noImportCss,
|
2424
|
+
rules_no_then_catch_finally: noThenCatchFinally,
|
2425
|
+
rules_no_unnecessary_template_literals: noUnnecessaryTemplateLiterals,
|
2426
|
+
rules_react_better_exhaustive_deps: reactBetterExhaustiveDeps,
|
2427
|
+
rules_react_hook_use_ref: reactHookUseRef,
|
2428
|
+
rules_react_prefer_named_property_access: reactPreferNamedPropertyAccess,
|
2429
|
+
rules_react_prefer_sx_prop: reactPreferSxProp,
|
2430
|
+
rules_tss_class_naming: tssClassNaming,
|
2431
|
+
rules_tss_no_color_name: tssNoColorName,
|
2432
|
+
rules_tss_no_color_value: tssNoColorValue,
|
2433
|
+
rules_tss_unused_classes: tssUnusedClasses
|
2690
2434
|
});
|
2691
2435
|
|
2692
2436
|
const plugin = {
|
@@ -2704,13 +2448,12 @@ const plugin = {
|
|
2704
2448
|
}
|
2705
2449
|
};
|
2706
2450
|
Object.keys(ruleFiles).forEach(key => {
|
2707
|
-
const ruleKey = key.replace(/^rules_/, '')
|
2451
|
+
const ruleKey = key.replace(/^rules_/, '');
|
2708
2452
|
const finalKey = ruleKey.replace(/_/g, '-');
|
2709
2453
|
const rule = ruleFiles[key];
|
2710
2454
|
plugin.rules[finalKey] = rule;
|
2711
2455
|
if (rule.meta && rule.meta.docs && rule.meta.docs.recommended) {
|
2712
|
-
plugin.configs.recommended.rules[
|
2713
|
-
rule.meta.type === 'suggestion' ? 'warn' : 'error';
|
2456
|
+
plugin.configs.recommended.rules["@agilebot/".concat(finalKey)] = rule.meta.type === 'suggestion' ? 'warn' : 'error';
|
2714
2457
|
}
|
2715
2458
|
});
|
2716
2459
|
|