@agilebot/eslint-plugin 0.3.5 → 0.3.7
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.d.ts +6 -3
- package/dist/index.js +842 -1001
- package/package.json +6 -5
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.7
|
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,19 @@ 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
|
-
|
87
|
-
|
79
|
+
const warned = new Set();
|
80
|
+
function warnOnce(message) {
|
81
|
+
if (warned.has(message)) {
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
warned.add(message);
|
85
|
+
console.warn("Warning: ".concat(message));
|
86
|
+
}
|
87
|
+
|
88
|
+
var importMonorepo = {
|
88
89
|
meta: {
|
89
90
|
type: 'problem',
|
90
91
|
docs: {
|
@@ -102,13 +103,10 @@ var monorepo = {
|
|
102
103
|
ImportDeclaration(node) {
|
103
104
|
let prefix = getSetting(context, 'monorepo-scope');
|
104
105
|
if (!prefix) {
|
105
|
-
|
106
|
-
console.error('Warning: agilebot/monorepo-scope is not set.');
|
107
|
-
warnedForMissingPrefix = true;
|
108
|
-
}
|
106
|
+
warnOnce('agilebot/monorepo-scope is not set.');
|
109
107
|
return;
|
110
108
|
}
|
111
|
-
prefix =
|
109
|
+
prefix = "".concat(prefix, "/");
|
112
110
|
if (typeof node.source.value !== 'string') {
|
113
111
|
return;
|
114
112
|
}
|
@@ -124,10 +122,8 @@ var monorepo = {
|
|
124
122
|
module: node.source.value
|
125
123
|
},
|
126
124
|
fix: fixer => {
|
127
|
-
const correctedPath = values
|
128
|
-
|
129
|
-
.join('/');
|
130
|
-
return fixer.replaceText(node.source, `'${correctedPath}'`);
|
125
|
+
const correctedPath = values.filter((_, index) => index !== 2).join('/');
|
126
|
+
return fixer.replaceText(node.source, "'".concat(correctedPath, "'"));
|
131
127
|
}
|
132
128
|
});
|
133
129
|
}
|
@@ -137,74 +133,33 @@ var monorepo = {
|
|
137
133
|
};
|
138
134
|
|
139
135
|
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
|
-
);
|
136
|
+
if (node.type === 'CallExpression' && (node.callee.name === 'formatMessage' || node.callee.name === '$t') && node.arguments.length > 0 && node.arguments[0].properties) {
|
137
|
+
return node.arguments[0].properties.find(a => a.key && a.key.name === attrName);
|
149
138
|
}
|
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
|
-
);
|
139
|
+
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')) {
|
140
|
+
return node.arguments[0].properties.find(a => a.key && a.key.name === attrName);
|
161
141
|
}
|
162
142
|
}
|
163
143
|
function findFormattedMessageAttrNode(node, attrName) {
|
164
|
-
if (
|
165
|
-
node.type === 'JSXIdentifier' &&
|
166
|
-
node.name === 'FormattedMessage' &&
|
167
|
-
node.parent &&
|
168
|
-
node.parent.type === 'JSXOpeningElement'
|
169
|
-
) {
|
144
|
+
if (node.type === 'JSXIdentifier' && node.name === 'FormattedMessage' && node.parent && node.parent.type === 'JSXOpeningElement') {
|
170
145
|
return node.parent.attributes.find(a => a.name && a.name.name === attrName);
|
171
146
|
}
|
172
147
|
}
|
173
148
|
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
|
-
) {
|
149
|
+
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
150
|
return node;
|
185
151
|
}
|
186
152
|
}
|
187
153
|
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
|
-
) {
|
154
|
+
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
155
|
return node;
|
197
156
|
}
|
198
157
|
}
|
199
158
|
function sortedTemplateElements(node) {
|
200
|
-
return [...node.quasis, ...node.expressions].sort(
|
201
|
-
(a, b) => a.range[0] - b.range[0]
|
202
|
-
);
|
159
|
+
return [...node.quasis, ...node.expressions].sort((a, b) => a.range[0] - b.range[0]);
|
203
160
|
}
|
204
161
|
function templateLiteralDisplayStr(node) {
|
205
|
-
return sortedTemplateElements(node)
|
206
|
-
.map(e => (!e.value ? '*' : e.value.raw))
|
207
|
-
.join('');
|
162
|
+
return sortedTemplateElements(node).map(e => !e.value ? '*' : e.value.raw).join('');
|
208
163
|
}
|
209
164
|
|
210
165
|
const localeFilesKeys = {};
|
@@ -218,22 +173,19 @@ function getIntlIds(context) {
|
|
218
173
|
localeFiles.forEach(f => {
|
219
174
|
const fullPath = projectRoot ? path__namespace.join(projectRoot, f) : f;
|
220
175
|
const mtime = fs__namespace.lstatSync(fullPath).mtime.getTime();
|
221
|
-
if (
|
222
|
-
!localeFilesKeys[fullPath] ||
|
223
|
-
mtime !== localeFilesKeys[fullPath].mtime
|
224
|
-
) {
|
176
|
+
if (!localeFilesKeys[fullPath] || mtime !== localeFilesKeys[fullPath].mtime) {
|
225
177
|
let json;
|
226
178
|
if (fullPath.endsWith('.json')) {
|
227
179
|
json = JSON.parse(fs__namespace.readFileSync(fullPath, 'utf8'));
|
228
180
|
} else if (fullPath.endsWith('.ts')) {
|
229
181
|
json = eslintUtils.tsImport(fullPath);
|
230
|
-
if (typeof json === 'object' && json
|
231
|
-
json = json
|
182
|
+
if (typeof json === 'object' && json["default"]) {
|
183
|
+
json = json["default"];
|
232
184
|
}
|
233
185
|
} else if (fullPath.endsWith('.js')) {
|
234
186
|
json = require(fullPath);
|
235
|
-
if (typeof json === 'object' && json
|
236
|
-
json = json
|
187
|
+
if (typeof json === 'object' && json["default"]) {
|
188
|
+
json = json["default"];
|
237
189
|
}
|
238
190
|
} else {
|
239
191
|
throw new Error('unsupported file extension');
|
@@ -248,7 +200,7 @@ function getIntlIds(context) {
|
|
248
200
|
return results;
|
249
201
|
}
|
250
202
|
|
251
|
-
var
|
203
|
+
var intlIdMissing = {
|
252
204
|
meta: {
|
253
205
|
docs: {
|
254
206
|
description: 'Validates intl message ids are in locale file',
|
@@ -275,9 +227,7 @@ var idMissing = {
|
|
275
227
|
}
|
276
228
|
}
|
277
229
|
function processTemplateLiteral(node) {
|
278
|
-
const exStr = sortedTemplateElements(node)
|
279
|
-
.map(e => (!e.value ? '.*' : e.value.raw))
|
280
|
-
.join('');
|
230
|
+
const exStr = sortedTemplateElements(node).map(e => !e.value ? '.*' : e.value.raw).join('');
|
281
231
|
const re = new RegExp(exStr);
|
282
232
|
if (!isTemplateTranslated(re)) {
|
283
233
|
context.report({
|
@@ -290,10 +240,7 @@ var idMissing = {
|
|
290
240
|
if (node.value.type === 'Literal') {
|
291
241
|
return processLiteral(node.value);
|
292
242
|
}
|
293
|
-
if (
|
294
|
-
node.value.type === 'JSXExpressionContainer' &&
|
295
|
-
node.value.expression.type === 'TemplateLiteral'
|
296
|
-
) {
|
243
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
297
244
|
return processTemplateLiteral(node.value.expression);
|
298
245
|
}
|
299
246
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -318,9 +265,7 @@ var idMissing = {
|
|
318
265
|
}
|
319
266
|
},
|
320
267
|
Property: function (node) {
|
321
|
-
const attrNode =
|
322
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
323
|
-
findAttrNodeInDefineMessage(node, 'id');
|
268
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
324
269
|
if (attrNode) {
|
325
270
|
return processAttrNode(attrNode);
|
326
271
|
}
|
@@ -329,33 +274,122 @@ var idMissing = {
|
|
329
274
|
}
|
330
275
|
};
|
331
276
|
|
332
|
-
var
|
277
|
+
var intlIdNaming = {
|
333
278
|
meta: {
|
334
279
|
docs: {
|
335
|
-
description: 'Validates intl message ids
|
280
|
+
description: 'Validates intl message ids naming convention',
|
336
281
|
category: 'Intl'
|
337
282
|
},
|
338
283
|
fixable: undefined,
|
339
|
-
schema: [
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
284
|
+
schema: [{
|
285
|
+
type: 'object',
|
286
|
+
properties: {
|
287
|
+
format: {
|
288
|
+
"enum": ['camelCase', 'PascalCase']
|
344
289
|
}
|
345
290
|
}
|
346
|
-
]
|
291
|
+
}],
|
292
|
+
messages: {
|
293
|
+
invalidIdNaming: "Invalid id naming, expected {{format}}"
|
294
|
+
}
|
295
|
+
},
|
296
|
+
create: function (context) {
|
297
|
+
if (!context.options[0]) {
|
298
|
+
throw new Error('Missing options');
|
299
|
+
}
|
300
|
+
const format = context.options[0].format;
|
301
|
+
function report(node, value) {
|
302
|
+
const values = value.split('.');
|
303
|
+
let isPass;
|
304
|
+
for (const v of values) {
|
305
|
+
switch (format) {
|
306
|
+
case 'camelCase':
|
307
|
+
if (!eslintUtils.isCamelCase(v)) {
|
308
|
+
isPass = false;
|
309
|
+
}
|
310
|
+
break;
|
311
|
+
case 'PascalCase':
|
312
|
+
if (!eslintUtils.isPascalCase(v)) {
|
313
|
+
isPass = false;
|
314
|
+
}
|
315
|
+
break;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
if (isPass === false) {
|
319
|
+
context.report({
|
320
|
+
node: node,
|
321
|
+
messageId: 'invalidIdNaming',
|
322
|
+
data: {
|
323
|
+
format
|
324
|
+
}
|
325
|
+
});
|
326
|
+
}
|
327
|
+
}
|
328
|
+
function processLiteral(node) {
|
329
|
+
report(node, node.value);
|
330
|
+
}
|
331
|
+
function processTemplateLiteral(node) {
|
332
|
+
const displayStr = templateLiteralDisplayStr(node);
|
333
|
+
report(node, displayStr);
|
334
|
+
}
|
335
|
+
function processAttrNode(node) {
|
336
|
+
if (node.value.type === 'Literal') {
|
337
|
+
return processLiteral(node.value);
|
338
|
+
}
|
339
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
340
|
+
return processTemplateLiteral(node.value.expression);
|
341
|
+
}
|
342
|
+
if (node.value.type === 'TemplateLiteral') {
|
343
|
+
return processTemplateLiteral(node.value);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
return {
|
347
|
+
JSXIdentifier: function (node) {
|
348
|
+
const attrNode = findFormattedMessageAttrNode(node, 'id');
|
349
|
+
if (attrNode) {
|
350
|
+
return processAttrNode(attrNode);
|
351
|
+
}
|
352
|
+
},
|
353
|
+
CallExpression: function (node) {
|
354
|
+
const attrNode = findFormatMessageAttrNode(node, 'id');
|
355
|
+
if (attrNode) {
|
356
|
+
return processAttrNode(attrNode);
|
357
|
+
}
|
358
|
+
},
|
359
|
+
Property: function (node) {
|
360
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
361
|
+
if (attrNode) {
|
362
|
+
return processAttrNode(attrNode);
|
363
|
+
}
|
364
|
+
}
|
365
|
+
};
|
366
|
+
}
|
367
|
+
};
|
368
|
+
|
369
|
+
var intlIdPrefix = {
|
370
|
+
meta: {
|
371
|
+
docs: {
|
372
|
+
description: 'Validates intl message ids has correct prefixes',
|
373
|
+
category: 'Intl'
|
374
|
+
},
|
375
|
+
fixable: undefined,
|
376
|
+
schema: [{
|
377
|
+
type: 'array',
|
378
|
+
items: {
|
379
|
+
type: 'string'
|
380
|
+
}
|
381
|
+
}]
|
347
382
|
},
|
348
383
|
create: function (context) {
|
349
384
|
if (context.options[0].length === 0) {
|
350
385
|
throw new Error('Prefixes are required in settings');
|
351
386
|
}
|
352
|
-
const hasPrefix = value =>
|
353
|
-
context.options[0].some(p => value.startsWith(p));
|
387
|
+
const hasPrefix = value => context.options[0].some(p => value.startsWith(p));
|
354
388
|
function report(node, value) {
|
355
389
|
if (!hasPrefix(value)) {
|
356
390
|
context.report({
|
357
391
|
node: node,
|
358
|
-
message:
|
392
|
+
message: "Invalid id prefix: ".concat(value)
|
359
393
|
});
|
360
394
|
}
|
361
395
|
}
|
@@ -370,10 +404,7 @@ var idPrefix = {
|
|
370
404
|
if (node.value.type === 'Literal') {
|
371
405
|
return processLiteral(node.value);
|
372
406
|
}
|
373
|
-
if (
|
374
|
-
node.value.type === 'JSXExpressionContainer' &&
|
375
|
-
node.value.expression.type === 'TemplateLiteral'
|
376
|
-
) {
|
407
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
377
408
|
return processTemplateLiteral(node.value.expression);
|
378
409
|
}
|
379
410
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -394,9 +425,7 @@ var idPrefix = {
|
|
394
425
|
}
|
395
426
|
},
|
396
427
|
Property: function (node) {
|
397
|
-
const attrNode =
|
398
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
399
|
-
findAttrNodeInDefineMessage(node, 'id');
|
428
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
400
429
|
if (attrNode) {
|
401
430
|
return processAttrNode(attrNode);
|
402
431
|
}
|
@@ -406,7 +435,7 @@ var idPrefix = {
|
|
406
435
|
};
|
407
436
|
|
408
437
|
const usedIds = new Map();
|
409
|
-
var
|
438
|
+
var intlIdUnused = {
|
410
439
|
meta: {
|
411
440
|
docs: {
|
412
441
|
description: 'Finds unused intl message ids in locale file',
|
@@ -438,9 +467,7 @@ var idUnused = {
|
|
438
467
|
}
|
439
468
|
}
|
440
469
|
function processTemplateLiteral(node) {
|
441
|
-
const exStr = sortedTemplateElements(node)
|
442
|
-
.map(e => (!e.value ? '.*' : e.value.raw))
|
443
|
-
.join('');
|
470
|
+
const exStr = sortedTemplateElements(node).map(e => !e.value ? '.*' : e.value.raw).join('');
|
444
471
|
const re = new RegExp(exStr);
|
445
472
|
if (isTemplateTranslated(re)) ;
|
446
473
|
}
|
@@ -448,10 +475,7 @@ var idUnused = {
|
|
448
475
|
if (node.value.type === 'Literal') {
|
449
476
|
return processLiteral(node.value);
|
450
477
|
}
|
451
|
-
if (
|
452
|
-
node.value.type === 'JSXExpressionContainer' &&
|
453
|
-
node.value.expression.type === 'TemplateLiteral'
|
454
|
-
) {
|
478
|
+
if (node.value.type === 'JSXExpressionContainer' && node.value.expression.type === 'TemplateLiteral') {
|
455
479
|
return processTemplateLiteral(node.value.expression);
|
456
480
|
}
|
457
481
|
if (node.value.type === 'TemplateLiteral') {
|
@@ -472,9 +496,7 @@ var idUnused = {
|
|
472
496
|
}
|
473
497
|
},
|
474
498
|
Property: function (node) {
|
475
|
-
const attrNode =
|
476
|
-
findAttrNodeInDefineMessages(node, 'id') ||
|
477
|
-
findAttrNodeInDefineMessage(node, 'id');
|
499
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'id') || findAttrNodeInDefineMessage(node, 'id');
|
478
500
|
if (attrNode) {
|
479
501
|
return processAttrNode(attrNode);
|
480
502
|
}
|
@@ -488,7 +510,7 @@ var idUnused = {
|
|
488
510
|
}
|
489
511
|
};
|
490
512
|
|
491
|
-
var
|
513
|
+
var intlNoDefault = {
|
492
514
|
meta: {
|
493
515
|
docs: {
|
494
516
|
description: 'Validates defaultMessage is not used with react-intl',
|
@@ -518,9 +540,7 @@ var noDefault = {
|
|
518
540
|
}
|
519
541
|
},
|
520
542
|
Property: function (node) {
|
521
|
-
const attrNode =
|
522
|
-
findAttrNodeInDefineMessages(node, 'defaultMessage') ||
|
523
|
-
findAttrNodeInDefineMessage(node, 'defaultMessage');
|
543
|
+
const attrNode = findAttrNodeInDefineMessages(node, 'defaultMessage') || findAttrNodeInDefineMessage(node, 'defaultMessage');
|
524
544
|
if (attrNode) {
|
525
545
|
return processAttrNode(attrNode);
|
526
546
|
}
|
@@ -529,79 +549,213 @@ var noDefault = {
|
|
529
549
|
}
|
530
550
|
};
|
531
551
|
|
532
|
-
var
|
552
|
+
var noAsyncArrayMethods = {
|
553
|
+
meta: {
|
554
|
+
docs: {
|
555
|
+
description: 'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
|
556
|
+
category: 'Array',
|
557
|
+
recommended: true
|
558
|
+
},
|
559
|
+
fixable: undefined,
|
560
|
+
schema: [],
|
561
|
+
messages: {
|
562
|
+
noAsyncArrayMethods: "No async function in method '{{ methodName }}'"
|
563
|
+
}
|
564
|
+
},
|
565
|
+
create: function (context) {
|
566
|
+
return {
|
567
|
+
ExpressionStatement: function (node) {
|
568
|
+
const notAllowedArrayMethods = ['forEach', 'filter', 'some', 'every', 'map', 'reduce', 'reduceRight', 'flatMap', 'find', 'findIndex', 'findLast', 'findLastIndex'];
|
569
|
+
const {
|
570
|
+
callee
|
571
|
+
} = node.expression;
|
572
|
+
if (!callee || !callee.property || !callee.property.name) {
|
573
|
+
return;
|
574
|
+
}
|
575
|
+
if (notAllowedArrayMethods.includes(callee.property.name)) {
|
576
|
+
const functionArguments = node.expression.arguments.find(n => {
|
577
|
+
return ['ArrowFunctionExpression', 'FunctionExpression'].includes(n.type);
|
578
|
+
});
|
579
|
+
if (functionArguments && functionArguments.async) {
|
580
|
+
context.report({
|
581
|
+
node,
|
582
|
+
messageId: 'noAsyncArrayMethods',
|
583
|
+
data: {
|
584
|
+
methodName: callee.property.name
|
585
|
+
}
|
586
|
+
});
|
587
|
+
}
|
588
|
+
}
|
589
|
+
}
|
590
|
+
};
|
591
|
+
}
|
592
|
+
};
|
593
|
+
|
594
|
+
var noImportCss = {
|
595
|
+
meta: {
|
596
|
+
type: 'problem',
|
597
|
+
docs: {
|
598
|
+
description: 'Prevent importing CSS',
|
599
|
+
recommended: true
|
600
|
+
},
|
601
|
+
fixable: 'code',
|
602
|
+
schema: [],
|
603
|
+
messages: {
|
604
|
+
noImportCSS: 'Do not import CSS files. Use CSS-in-JS instead.'
|
605
|
+
}
|
606
|
+
},
|
607
|
+
create(context) {
|
608
|
+
return {
|
609
|
+
ImportDeclaration(node) {
|
610
|
+
const ext = path.extname(node.source.value);
|
611
|
+
if (ext.startsWith('.css') || ext.startsWith('.scss') || ext.startsWith('.sass') || ext.startsWith('.less') || ext.startsWith('.styl')) {
|
612
|
+
context.report({
|
613
|
+
node,
|
614
|
+
messageId: 'noImportCSS',
|
615
|
+
data: {
|
616
|
+
path: node.source.value
|
617
|
+
}
|
618
|
+
});
|
619
|
+
}
|
620
|
+
}
|
621
|
+
};
|
622
|
+
}
|
623
|
+
};
|
624
|
+
|
625
|
+
var noThenCatchFinally = {
|
626
|
+
meta: {
|
627
|
+
type: 'suggestion',
|
628
|
+
docs: {
|
629
|
+
description: 'Disallow the use of then()/catch()/finally() when invoking specific functions.'
|
630
|
+
},
|
631
|
+
schema: [{
|
632
|
+
type: 'object',
|
633
|
+
properties: {
|
634
|
+
restrictedFunctions: {
|
635
|
+
type: 'array',
|
636
|
+
uniqueItems: true,
|
637
|
+
items: {
|
638
|
+
type: 'string'
|
639
|
+
}
|
640
|
+
}
|
641
|
+
}
|
642
|
+
}],
|
643
|
+
messages: {
|
644
|
+
forbiddenThenCatchFinally: "then()/catch()/finally() is forbidden when invoke {{ name }}()."
|
645
|
+
}
|
646
|
+
},
|
647
|
+
create(context) {
|
648
|
+
const configuration = context.options[0] || {};
|
649
|
+
const restrictedFunctions = configuration.restrictedFunctions || [];
|
650
|
+
function isTopLevelScoped() {
|
651
|
+
return context.getScope().block.type === 'Program';
|
652
|
+
}
|
653
|
+
function isThenCatchFinally(node) {
|
654
|
+
return node.property && (node.property.name === 'then' || node.property.name === 'catch' || node.property.name === 'finally');
|
655
|
+
}
|
656
|
+
return {
|
657
|
+
'CallExpression > MemberExpression.callee'(node) {
|
658
|
+
if (isTopLevelScoped()) {
|
659
|
+
return;
|
660
|
+
}
|
661
|
+
if (!isThenCatchFinally(node)) {
|
662
|
+
return;
|
663
|
+
}
|
664
|
+
const callExpression = node.object;
|
665
|
+
if (callExpression.type === 'CallExpression' && callExpression.callee.type === 'Identifier' && restrictedFunctions.includes(callExpression.callee.name)) {
|
666
|
+
context.report({
|
667
|
+
node: node.property,
|
668
|
+
messageId: 'forbiddenThenCatchFinally',
|
669
|
+
data: {
|
670
|
+
name: callExpression.callee.name
|
671
|
+
}
|
672
|
+
});
|
673
|
+
}
|
674
|
+
}
|
675
|
+
};
|
676
|
+
}
|
677
|
+
};
|
678
|
+
|
679
|
+
var noUnnecessaryTemplateLiterals = {
|
680
|
+
meta: {
|
681
|
+
type: 'problem',
|
682
|
+
docs: {
|
683
|
+
description: 'Check if a template string contains only one ${}',
|
684
|
+
recommended: true
|
685
|
+
},
|
686
|
+
fixable: 'code',
|
687
|
+
schema: [],
|
688
|
+
messages: {
|
689
|
+
unnecessaryTemplateString: 'Unnecessary template string with only one ${}.'
|
690
|
+
}
|
691
|
+
},
|
692
|
+
create(context) {
|
693
|
+
return {
|
694
|
+
TemplateLiteral(node) {
|
695
|
+
const code = context.sourceCode.getText(node);
|
696
|
+
if (code.startsWith('`${') && code.endsWith('}`') && code.split('${').length === 2) {
|
697
|
+
context.report({
|
698
|
+
node,
|
699
|
+
messageId: 'unnecessaryTemplateString',
|
700
|
+
fix(fixer) {
|
701
|
+
return fixer.replaceText(node, code.substring(3, code.length - 2));
|
702
|
+
}
|
703
|
+
});
|
704
|
+
}
|
705
|
+
}
|
706
|
+
};
|
707
|
+
}
|
708
|
+
};
|
709
|
+
|
710
|
+
var reactBetterExhaustiveDeps = {
|
533
711
|
meta: {
|
534
712
|
type: 'suggestion',
|
535
713
|
docs: {
|
536
|
-
description:
|
537
|
-
'verifies the list of dependencies for Hooks like useEffect and similar',
|
714
|
+
description: 'verifies the list of dependencies for Hooks like useEffect and similar',
|
538
715
|
url: 'https://github.com/facebook/react/issues/14920'
|
539
716
|
},
|
540
717
|
fixable: 'code',
|
541
718
|
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'
|
719
|
+
schema: [{
|
720
|
+
type: 'object',
|
721
|
+
additionalProperties: false,
|
722
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: false,
|
723
|
+
properties: {
|
724
|
+
additionalHooks: {
|
725
|
+
type: 'string'
|
726
|
+
},
|
727
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: {
|
728
|
+
type: 'boolean'
|
729
|
+
},
|
730
|
+
staticHooks: {
|
731
|
+
type: 'object',
|
732
|
+
additionalProperties: {
|
733
|
+
oneOf: [{
|
734
|
+
type: 'boolean'
|
735
|
+
}, {
|
736
|
+
type: 'array',
|
737
|
+
items: {
|
738
|
+
type: 'boolean'
|
739
|
+
}
|
740
|
+
}, {
|
741
|
+
type: 'object',
|
742
|
+
additionalProperties: {
|
743
|
+
type: 'boolean'
|
744
|
+
}
|
745
|
+
}]
|
578
746
|
}
|
747
|
+
},
|
748
|
+
checkMemoizedVariableIsStatic: {
|
749
|
+
type: 'boolean'
|
579
750
|
}
|
580
751
|
}
|
581
|
-
]
|
752
|
+
}]
|
582
753
|
},
|
583
754
|
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;
|
755
|
+
const additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : undefined;
|
756
|
+
const enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
|
757
|
+
const staticHooks = context.options && context.options[0] && context.options[0].staticHooks || {};
|
758
|
+
const checkMemoizedVariableIsStatic = context.options && context.options[0] && context.options[0].checkMemoizedVariableIsStatic || false;
|
605
759
|
const options = {
|
606
760
|
additionalHooks,
|
607
761
|
enableDangerousAutofixThisMayCauseInfiniteLoops,
|
@@ -609,11 +763,8 @@ var betterExhaustiveDeps = {
|
|
609
763
|
checkMemoizedVariableIsStatic
|
610
764
|
};
|
611
765
|
function reportProblem(problem) {
|
612
|
-
if (
|
613
|
-
|
614
|
-
Array.isArray(problem.suggest) &&
|
615
|
-
problem.suggest.length > 0
|
616
|
-
) {
|
766
|
+
if (enableDangerousAutofixThisMayCauseInfiniteLoops &&
|
767
|
+
Array.isArray(problem.suggest) && problem.suggest.length > 0) {
|
617
768
|
problem.fix = problem.suggest[0].fix;
|
618
769
|
}
|
619
770
|
context.report(problem);
|
@@ -633,28 +784,11 @@ var betterExhaustiveDeps = {
|
|
633
784
|
return result;
|
634
785
|
};
|
635
786
|
}
|
636
|
-
function visitFunctionWithDependencies(
|
637
|
-
node,
|
638
|
-
declaredDependenciesNode,
|
639
|
-
reactiveHook,
|
640
|
-
reactiveHookName,
|
641
|
-
isEffect
|
642
|
-
) {
|
787
|
+
function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
|
643
788
|
if (isEffect && node.async) {
|
644
789
|
reportProblem({
|
645
790
|
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'
|
791
|
+
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
792
|
});
|
659
793
|
}
|
660
794
|
const scope = scopeManager.acquire(node);
|
@@ -675,14 +809,8 @@ var betterExhaustiveDeps = {
|
|
675
809
|
componentScope = currentScope;
|
676
810
|
}
|
677
811
|
const isArray = Array.isArray;
|
678
|
-
const memoizedIsStableKnownHookValue = memoizeWithWeakMap(
|
679
|
-
|
680
|
-
stableKnownValueCache
|
681
|
-
);
|
682
|
-
const memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(
|
683
|
-
isFunctionWithoutCapturedValues,
|
684
|
-
functionWithoutCapturedValueCache
|
685
|
-
);
|
812
|
+
const memoizedIsStableKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
|
813
|
+
const memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache);
|
686
814
|
function isStableKnownHookValue(resolved) {
|
687
815
|
if (!isArray(resolved.defs)) {
|
688
816
|
return false;
|
@@ -709,40 +837,27 @@ var betterExhaustiveDeps = {
|
|
709
837
|
return false;
|
710
838
|
}
|
711
839
|
}
|
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
|
-
) {
|
840
|
+
if (declaration.kind === 'const' && init.type === 'Literal' && (typeof init.value === 'string' || typeof init.value === 'number' || init.value == null)) {
|
719
841
|
return true;
|
720
842
|
}
|
721
843
|
if (init.type !== 'CallExpression') {
|
722
844
|
return false;
|
723
845
|
}
|
724
846
|
let callee = init.callee;
|
725
|
-
if (
|
726
|
-
callee.type === 'MemberExpression' &&
|
727
|
-
callee.object.name === 'React' &&
|
728
|
-
callee.property != null &&
|
729
|
-
!callee.computed
|
730
|
-
) {
|
847
|
+
if (callee.type === 'MemberExpression' && callee.object.name === 'React' && callee.property != null && !callee.computed) {
|
731
848
|
callee = callee.property;
|
732
849
|
}
|
733
850
|
if (callee.type !== 'Identifier') {
|
734
851
|
return false;
|
735
852
|
}
|
736
853
|
const id = def.node.id;
|
737
|
-
const {
|
854
|
+
const {
|
855
|
+
name
|
856
|
+
} = callee;
|
738
857
|
if (name === 'useRef' && id.type === 'Identifier') {
|
739
858
|
return true;
|
740
859
|
} else if (name === 'useState' || name === 'useReducer') {
|
741
|
-
if (
|
742
|
-
id.type === 'ArrayPattern' &&
|
743
|
-
id.elements.length === 2 &&
|
744
|
-
isArray(resolved.identifiers)
|
745
|
-
) {
|
860
|
+
if (id.type === 'ArrayPattern' && id.elements.length === 2 && isArray(resolved.identifiers)) {
|
746
861
|
if (id.elements[1] === resolved.identifiers[0]) {
|
747
862
|
if (name === 'useState') {
|
748
863
|
const references = resolved.references;
|
@@ -769,18 +884,11 @@ var betterExhaustiveDeps = {
|
|
769
884
|
}
|
770
885
|
}
|
771
886
|
} 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
|
-
) {
|
887
|
+
if (id.type === 'ArrayPattern' && id.elements.length === 2 && Array.isArray(resolved.identifiers) &&
|
888
|
+
id.elements[1] === resolved.identifiers[0]) {
|
778
889
|
return true;
|
779
890
|
}
|
780
|
-
} else if (
|
781
|
-
options.checkMemoizedVariableIsStatic &&
|
782
|
-
(name === 'useMemo' || name === 'useCallback')
|
783
|
-
) {
|
891
|
+
} else if (options.checkMemoizedVariableIsStatic && (name === 'useMemo' || name === 'useCallback')) {
|
784
892
|
const hookArgs = callee.parent.arguments;
|
785
893
|
if (hookArgs.length < 2) {
|
786
894
|
return false;
|
@@ -790,13 +898,8 @@ var betterExhaustiveDeps = {
|
|
790
898
|
return true;
|
791
899
|
}
|
792
900
|
for (const dependencyNode of dependencies) {
|
793
|
-
const dependencyRefernece = resolved.scope.references.find(
|
794
|
-
|
795
|
-
);
|
796
|
-
if (
|
797
|
-
dependencyRefernece !== undefined &&
|
798
|
-
memoizedIsStableKnownHookValue(dependencyRefernece.resolved)
|
799
|
-
) {
|
901
|
+
const dependencyRefernece = resolved.scope.references.find(reference => reference.identifier === dependencyNode);
|
902
|
+
if (dependencyRefernece !== undefined && memoizedIsStableKnownHookValue(dependencyRefernece.resolved)) {
|
800
903
|
continue;
|
801
904
|
} else {
|
802
905
|
return false;
|
@@ -805,11 +908,7 @@ var betterExhaustiveDeps = {
|
|
805
908
|
return true;
|
806
909
|
} else {
|
807
910
|
Object.entries(options.staticHooks).forEach(([key, staticParts]) => {
|
808
|
-
if (
|
809
|
-
typeof staticParts === 'object' &&
|
810
|
-
staticParts.regexp &&
|
811
|
-
new RegExp(key).test(name)
|
812
|
-
) {
|
911
|
+
if (typeof staticParts === 'object' && staticParts.regexp && new RegExp(key).test(name)) {
|
813
912
|
options.staticHooks[name] = staticParts.value;
|
814
913
|
}
|
815
914
|
});
|
@@ -818,23 +917,14 @@ var betterExhaustiveDeps = {
|
|
818
917
|
if (staticParts === true) {
|
819
918
|
return true;
|
820
919
|
} else if (Array.isArray(staticParts)) {
|
821
|
-
if (
|
822
|
-
id.type === 'ArrayPattern' &&
|
823
|
-
id.elements.length <= staticParts.length &&
|
824
|
-
Array.isArray(resolved.identifiers)
|
825
|
-
) {
|
920
|
+
if (id.type === 'ArrayPattern' && id.elements.length <= staticParts.length && Array.isArray(resolved.identifiers)) {
|
826
921
|
const idx = id.elements.indexOf(resolved.identifiers[0]);
|
827
922
|
if (idx >= 0) {
|
828
923
|
return staticParts[idx];
|
829
924
|
}
|
830
925
|
}
|
831
|
-
} else if (
|
832
|
-
|
833
|
-
id.type === 'ObjectPattern'
|
834
|
-
) {
|
835
|
-
const property = id.properties.find(
|
836
|
-
p => p.key === resolved.identifiers[0]
|
837
|
-
);
|
926
|
+
} else if (typeof staticParts === 'object' && id.type === 'ObjectPattern') {
|
927
|
+
const property = id.properties.find(p => p.key === resolved.identifiers[0]);
|
838
928
|
if (property) {
|
839
929
|
return staticParts[property.key.name];
|
840
930
|
}
|
@@ -862,11 +952,8 @@ var betterExhaustiveDeps = {
|
|
862
952
|
const childScope = childScopes[i];
|
863
953
|
const childScopeBlock = childScope.block;
|
864
954
|
if (
|
865
|
-
|
866
|
-
|
867
|
-
(fnNode.type === 'VariableDeclarator' &&
|
868
|
-
childScopeBlock.parent === fnNode)
|
869
|
-
) {
|
955
|
+
fnNode.type === 'FunctionDeclaration' && childScopeBlock === fnNode ||
|
956
|
+
fnNode.type === 'VariableDeclarator' && childScopeBlock.parent === fnNode) {
|
870
957
|
fnScope = childScope;
|
871
958
|
break;
|
872
959
|
}
|
@@ -879,10 +966,8 @@ var betterExhaustiveDeps = {
|
|
879
966
|
if (ref.resolved == null) {
|
880
967
|
continue;
|
881
968
|
}
|
882
|
-
if (
|
883
|
-
|
884
|
-
!memoizedIsStableKnownHookValue(ref.resolved)
|
885
|
-
) {
|
969
|
+
if (pureScopes.has(ref.resolved.scope) &&
|
970
|
+
!memoizedIsStableKnownHookValue(ref.resolved)) {
|
886
971
|
return false;
|
887
972
|
}
|
888
973
|
}
|
@@ -894,9 +979,7 @@ var betterExhaustiveDeps = {
|
|
894
979
|
let isInReturnedFunction = false;
|
895
980
|
while (curScope.block !== node) {
|
896
981
|
if (curScope.type === 'function') {
|
897
|
-
isInReturnedFunction =
|
898
|
-
curScope.block.parent != null &&
|
899
|
-
curScope.block.parent.type === 'ReturnStatement';
|
982
|
+
isInReturnedFunction = curScope.block.parent != null && curScope.block.parent.type === 'ReturnStatement';
|
900
983
|
}
|
901
984
|
curScope = curScope.upper;
|
902
985
|
}
|
@@ -913,34 +996,19 @@ var betterExhaustiveDeps = {
|
|
913
996
|
if (!pureScopes.has(reference.resolved.scope)) {
|
914
997
|
continue;
|
915
998
|
}
|
916
|
-
const referenceNode = fastFindReferenceWithParent(
|
917
|
-
node,
|
918
|
-
reference.identifier
|
919
|
-
);
|
999
|
+
const referenceNode = fastFindReferenceWithParent(node, reference.identifier);
|
920
1000
|
const dependencyNode = getDependency(referenceNode);
|
921
|
-
const dependency = analyzePropertyChain(
|
922
|
-
dependencyNode,
|
923
|
-
optionalChains
|
924
|
-
);
|
1001
|
+
const dependency = analyzePropertyChain(dependencyNode, optionalChains);
|
925
1002
|
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
|
-
) {
|
1003
|
+
isEffect &&
|
1004
|
+
dependencyNode.type === 'Identifier' && (dependencyNode.parent.type === 'MemberExpression' || dependencyNode.parent.type === 'OptionalMemberExpression') && !dependencyNode.parent.computed && dependencyNode.parent.property.type === 'Identifier' && dependencyNode.parent.property.name === 'current' &&
|
1005
|
+
isInsideEffectCleanup(reference)) {
|
935
1006
|
currentRefsInEffectCleanup.set(dependency, {
|
936
1007
|
reference,
|
937
1008
|
dependencyNode
|
938
1009
|
});
|
939
1010
|
}
|
940
|
-
if (
|
941
|
-
dependencyNode.parent.type === 'TSTypeQuery' ||
|
942
|
-
dependencyNode.parent.type === 'TSTypeReference'
|
943
|
-
) {
|
1011
|
+
if (dependencyNode.parent.type === 'TSTypeQuery' || dependencyNode.parent.type === 'TSTypeReference') {
|
944
1012
|
continue;
|
945
1013
|
}
|
946
1014
|
const def = reference.resolved.defs[0];
|
@@ -955,9 +1023,7 @@ var betterExhaustiveDeps = {
|
|
955
1023
|
}
|
956
1024
|
if (!dependencies.has(dependency)) {
|
957
1025
|
const resolved = reference.resolved;
|
958
|
-
const isStable =
|
959
|
-
memoizedIsStableKnownHookValue(resolved) ||
|
960
|
-
memoizedIsFunctionWithoutCapturedValues(resolved);
|
1026
|
+
const isStable = memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
|
961
1027
|
dependencies.set(dependency, {
|
962
1028
|
isStable,
|
963
1029
|
references: [reference]
|
@@ -970,39 +1036,33 @@ var betterExhaustiveDeps = {
|
|
970
1036
|
gatherDependenciesRecursively(childScope);
|
971
1037
|
}
|
972
1038
|
}
|
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;
|
1039
|
+
currentRefsInEffectCleanup.forEach(({
|
1040
|
+
reference,
|
1041
|
+
dependencyNode
|
1042
|
+
}, dependency) => {
|
1043
|
+
const references = reference.resolved.references;
|
1044
|
+
let foundCurrentAssignment = false;
|
1045
|
+
for (const {
|
1046
|
+
identifier
|
1047
|
+
} of references) {
|
1048
|
+
const {
|
1049
|
+
parent
|
1050
|
+
} = identifier;
|
1051
|
+
if (parent != null &&
|
1052
|
+
parent.type === 'MemberExpression' && !parent.computed && parent.property.type === 'Identifier' && parent.property.name === 'current' &&
|
1053
|
+
parent.parent.type === 'AssignmentExpression' && parent.parent.left === parent) {
|
1054
|
+
foundCurrentAssignment = true;
|
1055
|
+
break;
|
994
1056
|
}
|
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
1057
|
}
|
1005
|
-
|
1058
|
+
if (foundCurrentAssignment) {
|
1059
|
+
return;
|
1060
|
+
}
|
1061
|
+
reportProblem({
|
1062
|
+
node: dependencyNode.parent.property,
|
1063
|
+
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."
|
1064
|
+
});
|
1065
|
+
});
|
1006
1066
|
const staleAssignments = new Set();
|
1007
1067
|
function reportStaleAssignment(writeExpr, key) {
|
1008
1068
|
if (staleAssignments.has(key)) {
|
@@ -1011,17 +1071,14 @@ var betterExhaustiveDeps = {
|
|
1011
1071
|
staleAssignments.add(key);
|
1012
1072
|
reportProblem({
|
1013
1073
|
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)}.`
|
1074
|
+
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
1075
|
});
|
1022
1076
|
}
|
1023
1077
|
const stableDependencies = new Set();
|
1024
|
-
dependencies.forEach(({
|
1078
|
+
dependencies.forEach(({
|
1079
|
+
isStable,
|
1080
|
+
references
|
1081
|
+
}, key) => {
|
1025
1082
|
if (isStable) {
|
1026
1083
|
stableDependencies.add(key);
|
1027
1084
|
}
|
@@ -1036,7 +1093,9 @@ var betterExhaustiveDeps = {
|
|
1036
1093
|
}
|
1037
1094
|
if (!declaredDependenciesNode) {
|
1038
1095
|
let setStateInsideEffectWithoutDeps = null;
|
1039
|
-
dependencies.forEach(({
|
1096
|
+
dependencies.forEach(({
|
1097
|
+
references
|
1098
|
+
}, key) => {
|
1040
1099
|
if (setStateInsideEffectWithoutDeps) {
|
1041
1100
|
return;
|
1042
1101
|
}
|
@@ -1060,7 +1119,9 @@ var betterExhaustiveDeps = {
|
|
1060
1119
|
});
|
1061
1120
|
});
|
1062
1121
|
if (setStateInsideEffectWithoutDeps) {
|
1063
|
-
const {
|
1122
|
+
const {
|
1123
|
+
suggestedDependencies
|
1124
|
+
} = collectRecommendations({
|
1064
1125
|
dependencies,
|
1065
1126
|
declaredDependencies: [],
|
1066
1127
|
stableDependencies,
|
@@ -1069,25 +1130,13 @@ var betterExhaustiveDeps = {
|
|
1069
1130
|
});
|
1070
1131
|
reportProblem({
|
1071
1132
|
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
|
-
}
|
1133
|
+
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."),
|
1134
|
+
suggest: [{
|
1135
|
+
desc: "Add dependencies array: [".concat(suggestedDependencies.join(', '), "]"),
|
1136
|
+
fix(fixer) {
|
1137
|
+
return fixer.insertTextAfter(node, ", [".concat(suggestedDependencies.join(', '), "]"));
|
1089
1138
|
}
|
1090
|
-
]
|
1139
|
+
}]
|
1091
1140
|
});
|
1092
1141
|
}
|
1093
1142
|
return;
|
@@ -1097,11 +1146,7 @@ var betterExhaustiveDeps = {
|
|
1097
1146
|
if (declaredDependenciesNode.type !== 'ArrayExpression') {
|
1098
1147
|
reportProblem({
|
1099
1148
|
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.'
|
1149
|
+
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
1150
|
});
|
1106
1151
|
} else {
|
1107
1152
|
declaredDependenciesNode.elements.forEach(declaredDependencyNode => {
|
@@ -1111,46 +1156,31 @@ var betterExhaustiveDeps = {
|
|
1111
1156
|
if (declaredDependencyNode.type === 'SpreadElement') {
|
1112
1157
|
reportProblem({
|
1113
1158
|
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.'
|
1159
|
+
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
1160
|
});
|
1120
1161
|
return;
|
1121
1162
|
}
|
1122
1163
|
let declaredDependency;
|
1123
1164
|
try {
|
1124
|
-
declaredDependency = analyzePropertyChain(
|
1125
|
-
declaredDependencyNode,
|
1126
|
-
null
|
1127
|
-
);
|
1165
|
+
declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
|
1128
1166
|
} catch (err) {
|
1129
1167
|
if (/Unsupported node type/.test(err.message)) {
|
1130
1168
|
if (declaredDependencyNode.type === 'Literal') {
|
1131
1169
|
if (dependencies.has(declaredDependencyNode.value)) {
|
1132
1170
|
reportProblem({
|
1133
1171
|
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?`
|
1172
|
+
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
1173
|
});
|
1139
1174
|
} else {
|
1140
1175
|
reportProblem({
|
1141
1176
|
node: declaredDependencyNode,
|
1142
|
-
message:
|
1143
|
-
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
|
1144
|
-
'because it never changes. You can safely remove it.'
|
1177
|
+
message: "The ".concat(declaredDependencyNode.raw, " literal is not a valid dependency ") + 'because it never changes. You can safely remove it.'
|
1145
1178
|
});
|
1146
1179
|
}
|
1147
1180
|
} else {
|
1148
1181
|
reportProblem({
|
1149
1182
|
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.'
|
1183
|
+
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
1184
|
});
|
1155
1185
|
}
|
1156
1186
|
return;
|
@@ -1158,16 +1188,10 @@ var betterExhaustiveDeps = {
|
|
1158
1188
|
throw err;
|
1159
1189
|
}
|
1160
1190
|
let maybeID = declaredDependencyNode;
|
1161
|
-
while (
|
1162
|
-
maybeID.type === 'MemberExpression' ||
|
1163
|
-
maybeID.type === 'OptionalMemberExpression' ||
|
1164
|
-
maybeID.type === 'ChainExpression'
|
1165
|
-
) {
|
1191
|
+
while (maybeID.type === 'MemberExpression' || maybeID.type === 'OptionalMemberExpression' || maybeID.type === 'ChainExpression') {
|
1166
1192
|
maybeID = maybeID.object || maybeID.expression.object;
|
1167
1193
|
}
|
1168
|
-
const isDeclaredInComponent = !componentScope.through.some(
|
1169
|
-
ref => ref.identifier === maybeID
|
1170
|
-
);
|
1194
|
+
const isDeclaredInComponent = !componentScope.through.some(ref => ref.identifier === maybeID);
|
1171
1195
|
declaredDependencies.push({
|
1172
1196
|
key: declaredDependency,
|
1173
1197
|
node: declaredDependencyNode
|
@@ -1190,10 +1214,7 @@ var betterExhaustiveDeps = {
|
|
1190
1214
|
isEffect
|
1191
1215
|
});
|
1192
1216
|
let suggestedDeps = suggestedDependencies;
|
1193
|
-
const problemCount =
|
1194
|
-
duplicateDependencies.size +
|
1195
|
-
missingDependencies.size +
|
1196
|
-
unnecessaryDependencies.size;
|
1217
|
+
const problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
|
1197
1218
|
if (problemCount === 0) {
|
1198
1219
|
const constructions = scanForConstructions({
|
1199
1220
|
declaredDependencies,
|
@@ -1201,53 +1222,36 @@ var betterExhaustiveDeps = {
|
|
1201
1222
|
componentScope,
|
1202
1223
|
scope
|
1203
1224
|
});
|
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
|
-
});
|
1225
|
+
constructions.forEach(({
|
1226
|
+
construction,
|
1227
|
+
isUsedOutsideOfHook,
|
1228
|
+
depType
|
1229
|
+
}) => {
|
1230
|
+
const wrapperHook = depType === 'function' ? 'useCallback' : 'useMemo';
|
1231
|
+
const constructionType = depType === 'function' ? 'definition' : 'initialization';
|
1232
|
+
const defaultAdvice = "wrap the ".concat(constructionType, " of '").concat(construction.name.name, "' in its own ").concat(wrapperHook, "() Hook.");
|
1233
|
+
const advice = isUsedOutsideOfHook ? "To fix this, ".concat(defaultAdvice) : "Move it inside the ".concat(reactiveHookName, " callback. Alternatively, ").concat(defaultAdvice);
|
1234
|
+
const causation = depType === 'conditional' || depType === 'logical expression' ? 'could make' : 'makes';
|
1235
|
+
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);
|
1236
|
+
let suggest;
|
1237
|
+
if (isUsedOutsideOfHook && construction.type === 'Variable' &&
|
1238
|
+
depType === 'function') {
|
1239
|
+
suggest = [{
|
1240
|
+
desc: "Wrap the ".concat(constructionType, " of '").concat(construction.name.name, "' in its own ").concat(wrapperHook, "() Hook."),
|
1241
|
+
fix(fixer) {
|
1242
|
+
const [before, after] = wrapperHook === 'useMemo' ? ["useMemo(() => { return ", '; })'] : ['useCallback(', ')'];
|
1243
|
+
return [
|
1244
|
+
fixer.insertTextBefore(construction.node.init, before),
|
1245
|
+
fixer.insertTextAfter(construction.node.init, after)];
|
1246
|
+
}
|
1247
|
+
}];
|
1249
1248
|
}
|
1250
|
-
|
1249
|
+
reportProblem({
|
1250
|
+
node: construction.node,
|
1251
|
+
message,
|
1252
|
+
suggest
|
1253
|
+
});
|
1254
|
+
});
|
1251
1255
|
return;
|
1252
1256
|
}
|
1253
1257
|
if (!isEffect && missingDependencies.size > 0) {
|
@@ -1287,19 +1291,7 @@ var betterExhaustiveDeps = {
|
|
1287
1291
|
if (deps.size === 0) {
|
1288
1292
|
return null;
|
1289
1293
|
}
|
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
|
-
);
|
1294
|
+
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
1295
|
}
|
1304
1296
|
let extraWarning = '';
|
1305
1297
|
if (unnecessaryDependencies.size > 0) {
|
@@ -1313,15 +1305,11 @@ var betterExhaustiveDeps = {
|
|
1313
1305
|
}
|
1314
1306
|
});
|
1315
1307
|
if (badRef != null) {
|
1316
|
-
extraWarning =
|
1317
|
-
` Mutable values like '${badRef}' aren't valid dependencies ` +
|
1318
|
-
"because mutating them doesn't re-render the component.";
|
1308
|
+
extraWarning = " Mutable values like '".concat(badRef, "' aren't valid dependencies ") + "because mutating them doesn't re-render the component.";
|
1319
1309
|
} else if (externalDependencies.size > 0) {
|
1320
1310
|
const dep = [...externalDependencies][0];
|
1321
1311
|
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.`;
|
1312
|
+
extraWarning = " Outer scope values like '".concat(dep, "' aren't valid dependencies ") + "because mutating them doesn't re-render the component.";
|
1325
1313
|
}
|
1326
1314
|
}
|
1327
1315
|
}
|
@@ -1336,10 +1324,7 @@ var betterExhaustiveDeps = {
|
|
1336
1324
|
}
|
1337
1325
|
let isPropsOnlyUsedInMembers = true;
|
1338
1326
|
for (const ref of refs) {
|
1339
|
-
const id = fastFindReferenceWithParent(
|
1340
|
-
componentScope.block,
|
1341
|
-
ref.identifier
|
1342
|
-
);
|
1327
|
+
const id = fastFindReferenceWithParent(componentScope.block, ref.identifier);
|
1343
1328
|
if (!id) {
|
1344
1329
|
isPropsOnlyUsedInMembers = false;
|
1345
1330
|
break;
|
@@ -1349,20 +1334,13 @@ var betterExhaustiveDeps = {
|
|
1349
1334
|
isPropsOnlyUsedInMembers = false;
|
1350
1335
|
break;
|
1351
1336
|
}
|
1352
|
-
if (
|
1353
|
-
parent.type !== 'MemberExpression' &&
|
1354
|
-
parent.type !== 'OptionalMemberExpression'
|
1355
|
-
) {
|
1337
|
+
if (parent.type !== 'MemberExpression' && parent.type !== 'OptionalMemberExpression') {
|
1356
1338
|
isPropsOnlyUsedInMembers = false;
|
1357
1339
|
break;
|
1358
1340
|
}
|
1359
1341
|
}
|
1360
1342
|
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)}.`;
|
1343
|
+
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
1344
|
}
|
1367
1345
|
}
|
1368
1346
|
if (!extraWarning && missingDependencies.size > 0) {
|
@@ -1384,13 +1362,7 @@ var betterExhaustiveDeps = {
|
|
1384
1362
|
let id;
|
1385
1363
|
for (let i = 0; i < usedDep.references.length; i++) {
|
1386
1364
|
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
|
-
) {
|
1365
|
+
if (id != null && id.parent != null && (id.parent.type === 'CallExpression' || id.parent.type === 'OptionalCallExpression') && id.parent.callee === id) {
|
1394
1366
|
isFunctionCall = true;
|
1395
1367
|
break;
|
1396
1368
|
}
|
@@ -1401,10 +1373,7 @@ var betterExhaustiveDeps = {
|
|
1401
1373
|
missingCallbackDep = missingDep;
|
1402
1374
|
});
|
1403
1375
|
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.`;
|
1376
|
+
extraWarning = " If '".concat(missingCallbackDep, "' changes too often, ") + "find the parent component that defines it " + "and wrap that definition in useCallback.";
|
1408
1377
|
}
|
1409
1378
|
}
|
1410
1379
|
if (!extraWarning && missingDependencies.size > 0) {
|
@@ -1422,9 +1391,7 @@ var betterExhaustiveDeps = {
|
|
1422
1391
|
maybeCall = id.parent;
|
1423
1392
|
while (maybeCall != null && maybeCall !== componentScope.block) {
|
1424
1393
|
if (maybeCall.type === 'CallExpression') {
|
1425
|
-
const correspondingStateVariable = setStateCallSites.get(
|
1426
|
-
maybeCall.callee
|
1427
|
-
);
|
1394
|
+
const correspondingStateVariable = setStateCallSites.get(maybeCall.callee);
|
1428
1395
|
if (correspondingStateVariable != null) {
|
1429
1396
|
if (correspondingStateVariable.name === missingDep) {
|
1430
1397
|
setStateRecommendation = {
|
@@ -1464,27 +1431,13 @@ var betterExhaustiveDeps = {
|
|
1464
1431
|
if (setStateRecommendation != null) {
|
1465
1432
|
switch (setStateRecommendation.form) {
|
1466
1433
|
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}'.`;
|
1434
|
+
extraWarning = " You can also replace multiple useState variables with useReducer " + "if '".concat(setStateRecommendation.setter, "' needs the ") + "current value of '".concat(setStateRecommendation.missingDep, "'.");
|
1471
1435
|
break;
|
1472
1436
|
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.`;
|
1437
|
+
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
1438
|
break;
|
1479
1439
|
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.`;
|
1440
|
+
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
1441
|
break;
|
1489
1442
|
default:
|
1490
1443
|
throw new Error('Unknown case.');
|
@@ -1493,35 +1446,14 @@ var betterExhaustiveDeps = {
|
|
1493
1446
|
}
|
1494
1447
|
reportProblem({
|
1495
1448
|
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
|
-
]
|
1449
|
+
message: "React Hook ".concat(context.getSource(reactiveHook), " has ") + (
|
1450
|
+
getWarningMessage(missingDependencies, 'a', 'missing', 'include') || getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') || getWarningMessage(duplicateDependencies, 'a', 'duplicate', 'omit')) + extraWarning,
|
1451
|
+
suggest: [{
|
1452
|
+
desc: "Update the dependencies array to be: [".concat(suggestedDeps.map(element => formatDependency(element)).join(', '), "]"),
|
1453
|
+
fix(fixer) {
|
1454
|
+
return fixer.replaceText(declaredDependenciesNode, "[".concat(suggestedDeps.map(element => formatDependency(element)).join(', '), "]"));
|
1455
|
+
}
|
1456
|
+
}]
|
1525
1457
|
});
|
1526
1458
|
}
|
1527
1459
|
function visitCallExpression(node) {
|
@@ -1537,23 +1469,15 @@ var betterExhaustiveDeps = {
|
|
1537
1469
|
if (!callback) {
|
1538
1470
|
reportProblem({
|
1539
1471
|
node: reactiveHook,
|
1540
|
-
message:
|
1541
|
-
`React Hook ${reactiveHookName} requires an effect callback. ` +
|
1542
|
-
`Did you forget to pass a callback to the hook?`
|
1472
|
+
message: "React Hook ".concat(reactiveHookName, " requires an effect callback. ") + "Did you forget to pass a callback to the hook?"
|
1543
1473
|
});
|
1544
1474
|
return;
|
1545
1475
|
}
|
1546
1476
|
if (!declaredDependenciesNode && !isEffect) {
|
1547
|
-
if (
|
1548
|
-
reactiveHookName === 'useMemo' ||
|
1549
|
-
reactiveHookName === 'useCallback'
|
1550
|
-
) {
|
1477
|
+
if (reactiveHookName === 'useMemo' || reactiveHookName === 'useCallback') {
|
1551
1478
|
reportProblem({
|
1552
1479
|
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?`
|
1480
|
+
message: "React Hook ".concat(reactiveHookName, " does nothing when called with ") + "only one argument. Did you forget to pass an array of " + "dependencies?"
|
1557
1481
|
});
|
1558
1482
|
}
|
1559
1483
|
return;
|
@@ -1561,24 +1485,13 @@ var betterExhaustiveDeps = {
|
|
1561
1485
|
switch (callback.type) {
|
1562
1486
|
case 'FunctionExpression':
|
1563
1487
|
case 'ArrowFunctionExpression':
|
1564
|
-
visitFunctionWithDependencies(
|
1565
|
-
callback,
|
1566
|
-
declaredDependenciesNode,
|
1567
|
-
reactiveHook,
|
1568
|
-
reactiveHookName,
|
1569
|
-
isEffect
|
1570
|
-
);
|
1488
|
+
visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1571
1489
|
return;
|
1572
1490
|
case 'Identifier':
|
1573
1491
|
if (!declaredDependenciesNode) {
|
1574
1492
|
return;
|
1575
1493
|
}
|
1576
|
-
if (
|
1577
|
-
declaredDependenciesNode.elements &&
|
1578
|
-
declaredDependenciesNode.elements.some(
|
1579
|
-
el => el && el.type === 'Identifier' && el.name === callback.name
|
1580
|
-
)
|
1581
|
-
) {
|
1494
|
+
if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some(el => el && el.type === 'Identifier' && el.name === callback.name)) {
|
1582
1495
|
return;
|
1583
1496
|
}
|
1584
1497
|
const variable = context.getScope().set.get(callback.name);
|
@@ -1594,13 +1507,7 @@ var betterExhaustiveDeps = {
|
|
1594
1507
|
}
|
1595
1508
|
switch (def.node.type) {
|
1596
1509
|
case 'FunctionDeclaration':
|
1597
|
-
visitFunctionWithDependencies(
|
1598
|
-
def.node,
|
1599
|
-
declaredDependenciesNode,
|
1600
|
-
reactiveHook,
|
1601
|
-
reactiveHookName,
|
1602
|
-
isEffect
|
1603
|
-
);
|
1510
|
+
visitFunctionWithDependencies(def.node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1604
1511
|
return;
|
1605
1512
|
case 'VariableDeclarator':
|
1606
1513
|
const init = def.node.init;
|
@@ -1610,13 +1517,7 @@ var betterExhaustiveDeps = {
|
|
1610
1517
|
switch (init.type) {
|
1611
1518
|
case 'ArrowFunctionExpression':
|
1612
1519
|
case 'FunctionExpression':
|
1613
|
-
visitFunctionWithDependencies(
|
1614
|
-
init,
|
1615
|
-
declaredDependenciesNode,
|
1616
|
-
reactiveHook,
|
1617
|
-
reactiveHookName,
|
1618
|
-
isEffect
|
1619
|
-
);
|
1520
|
+
visitFunctionWithDependencies(init, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
|
1620
1521
|
return;
|
1621
1522
|
}
|
1622
1523
|
break;
|
@@ -1625,28 +1526,19 @@ var betterExhaustiveDeps = {
|
|
1625
1526
|
default:
|
1626
1527
|
reportProblem({
|
1627
1528
|
node: reactiveHook,
|
1628
|
-
message:
|
1629
|
-
`React Hook ${reactiveHookName} received a function whose dependencies ` +
|
1630
|
-
`are unknown. Pass an inline function instead.`
|
1529
|
+
message: "React Hook ".concat(reactiveHookName, " received a function whose dependencies ") + "are unknown. Pass an inline function instead."
|
1631
1530
|
});
|
1632
1531
|
return;
|
1633
1532
|
}
|
1634
1533
|
reportProblem({
|
1635
1534
|
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
|
-
}
|
1535
|
+
message: "React Hook ".concat(reactiveHookName, " has a missing dependency: '").concat(callback.name, "'. ") + "Either include it or remove the dependency array.",
|
1536
|
+
suggest: [{
|
1537
|
+
desc: "Update the dependencies array to be: [".concat(callback.name, "]"),
|
1538
|
+
fix(fixer) {
|
1539
|
+
return fixer.replaceText(declaredDependenciesNode, "[".concat(callback.name, "]"));
|
1648
1540
|
}
|
1649
|
-
]
|
1541
|
+
}]
|
1650
1542
|
});
|
1651
1543
|
}
|
1652
1544
|
return {
|
@@ -1677,7 +1569,9 @@ function collectRecommendations({
|
|
1677
1569
|
parent.isSubtreeUsed = true;
|
1678
1570
|
});
|
1679
1571
|
});
|
1680
|
-
declaredDependencies.forEach(({
|
1572
|
+
declaredDependencies.forEach(({
|
1573
|
+
key
|
1574
|
+
}) => {
|
1681
1575
|
const node = getOrCreateNodeByPath(depTree, key);
|
1682
1576
|
node.isSatisfiedRecursively = true;
|
1683
1577
|
});
|
@@ -1712,12 +1606,7 @@ function collectRecommendations({
|
|
1712
1606
|
}
|
1713
1607
|
const missingDependencies = new Set();
|
1714
1608
|
const satisfyingDependencies = new Set();
|
1715
|
-
scanTreeRecursively(
|
1716
|
-
depTree,
|
1717
|
-
missingDependencies,
|
1718
|
-
satisfyingDependencies,
|
1719
|
-
key => key
|
1720
|
-
);
|
1609
|
+
scanTreeRecursively(depTree, missingDependencies, satisfyingDependencies, key => key);
|
1721
1610
|
function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
|
1722
1611
|
node.children.forEach((child, key) => {
|
1723
1612
|
const path = keyToPath(key);
|
@@ -1731,29 +1620,22 @@ function collectRecommendations({
|
|
1731
1620
|
missingPaths.add(path);
|
1732
1621
|
return;
|
1733
1622
|
}
|
1734
|
-
scanTreeRecursively(
|
1735
|
-
child,
|
1736
|
-
missingPaths,
|
1737
|
-
satisfyingPaths,
|
1738
|
-
childKey => path + '.' + childKey
|
1739
|
-
);
|
1623
|
+
scanTreeRecursively(child, missingPaths, satisfyingPaths, childKey => path + '.' + childKey);
|
1740
1624
|
});
|
1741
1625
|
}
|
1742
1626
|
const suggestedDependencies = [];
|
1743
1627
|
const unnecessaryDependencies = new Set();
|
1744
1628
|
const duplicateDependencies = new Set();
|
1745
|
-
declaredDependencies.forEach(({
|
1629
|
+
declaredDependencies.forEach(({
|
1630
|
+
key
|
1631
|
+
}) => {
|
1746
1632
|
if (satisfyingDependencies.has(key)) {
|
1747
1633
|
if (!suggestedDependencies.includes(key)) {
|
1748
1634
|
suggestedDependencies.push(key);
|
1749
1635
|
} else {
|
1750
1636
|
duplicateDependencies.add(key);
|
1751
1637
|
}
|
1752
|
-
} else if (
|
1753
|
-
isEffect &&
|
1754
|
-
!key.endsWith('.current') &&
|
1755
|
-
!externalDependencies.has(key)
|
1756
|
-
) {
|
1638
|
+
} else if (isEffect && !key.endsWith('.current') && !externalDependencies.has(key)) {
|
1757
1639
|
if (!suggestedDependencies.includes(key)) {
|
1758
1640
|
suggestedDependencies.push(key);
|
1759
1641
|
}
|
@@ -1783,18 +1665,12 @@ function getConstructionExpressionType(node) {
|
|
1783
1665
|
case 'ClassExpression':
|
1784
1666
|
return 'class';
|
1785
1667
|
case 'ConditionalExpression':
|
1786
|
-
if (
|
1787
|
-
getConstructionExpressionType(node.consequent) != null ||
|
1788
|
-
getConstructionExpressionType(node.alternate) != null
|
1789
|
-
) {
|
1668
|
+
if (getConstructionExpressionType(node.consequent) != null || getConstructionExpressionType(node.alternate) != null) {
|
1790
1669
|
return 'conditional';
|
1791
1670
|
}
|
1792
1671
|
return null;
|
1793
1672
|
case 'LogicalExpression':
|
1794
|
-
if (
|
1795
|
-
getConstructionExpressionType(node.left) != null ||
|
1796
|
-
getConstructionExpressionType(node.right) != null
|
1797
|
-
) {
|
1673
|
+
if (getConstructionExpressionType(node.left) != null || getConstructionExpressionType(node.right) != null) {
|
1798
1674
|
return 'logical expression';
|
1799
1675
|
}
|
1800
1676
|
return null;
|
@@ -1827,41 +1703,32 @@ function scanForConstructions({
|
|
1827
1703
|
componentScope,
|
1828
1704
|
scope
|
1829
1705
|
}) {
|
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
|
-
}
|
1706
|
+
const constructions = declaredDependencies.map(({
|
1707
|
+
key
|
1708
|
+
}) => {
|
1709
|
+
const ref = componentScope.variables.find(v => v.name === key);
|
1710
|
+
if (ref == null) {
|
1862
1711
|
return null;
|
1863
|
-
}
|
1864
|
-
.
|
1712
|
+
}
|
1713
|
+
const node = ref.defs[0];
|
1714
|
+
if (node == null) {
|
1715
|
+
return null;
|
1716
|
+
}
|
1717
|
+
if (node.type === 'Variable' && node.node.type === 'VariableDeclarator' && node.node.id.type === 'Identifier' &&
|
1718
|
+
node.node.init != null) {
|
1719
|
+
const constantExpressionType = getConstructionExpressionType(node.node.init);
|
1720
|
+
if (constantExpressionType != null) {
|
1721
|
+
return [ref, constantExpressionType];
|
1722
|
+
}
|
1723
|
+
}
|
1724
|
+
if (node.type === 'FunctionName' && node.node.type === 'FunctionDeclaration') {
|
1725
|
+
return [ref, 'function'];
|
1726
|
+
}
|
1727
|
+
if (node.type === 'ClassName' && node.node.type === 'ClassDeclaration') {
|
1728
|
+
return [ref, 'class'];
|
1729
|
+
}
|
1730
|
+
return null;
|
1731
|
+
}).filter(Boolean);
|
1865
1732
|
function isUsedOutsideOfHook(ref) {
|
1866
1733
|
let foundWriteExpr = false;
|
1867
1734
|
for (let i = 0; i < ref.references.length; i++) {
|
@@ -1877,10 +1744,8 @@ function scanForConstructions({
|
|
1877
1744
|
while (currentScope !== scope && currentScope != null) {
|
1878
1745
|
currentScope = currentScope.upper;
|
1879
1746
|
}
|
1880
|
-
if (
|
1881
|
-
|
1882
|
-
!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)
|
1883
|
-
) {
|
1747
|
+
if (currentScope !== scope &&
|
1748
|
+
!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)) {
|
1884
1749
|
return true;
|
1885
1750
|
}
|
1886
1751
|
}
|
@@ -1893,26 +1758,10 @@ function scanForConstructions({
|
|
1893
1758
|
}));
|
1894
1759
|
}
|
1895
1760
|
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
|
-
) {
|
1761
|
+
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
1762
|
return getDependency(node.parent);
|
1910
1763
|
} else if (
|
1911
|
-
|
1912
|
-
node.parent &&
|
1913
|
-
node.parent.type === 'AssignmentExpression' &&
|
1914
|
-
node.parent.left === node
|
1915
|
-
) {
|
1764
|
+
node.type === 'MemberExpression' && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
|
1916
1765
|
return node.object;
|
1917
1766
|
}
|
1918
1767
|
return node;
|
@@ -1938,36 +1787,30 @@ function analyzePropertyChain(node, optionalChains) {
|
|
1938
1787
|
} else if (node.type === 'MemberExpression' && !node.computed) {
|
1939
1788
|
const object = analyzePropertyChain(node.object, optionalChains);
|
1940
1789
|
const property = analyzePropertyChain(node.property, null);
|
1941
|
-
const result =
|
1790
|
+
const result = "".concat(object, ".").concat(property);
|
1942
1791
|
markNode(node, optionalChains, result);
|
1943
1792
|
return result;
|
1944
1793
|
} else if (node.type === 'OptionalMemberExpression' && !node.computed) {
|
1945
1794
|
const object = analyzePropertyChain(node.object, optionalChains);
|
1946
1795
|
const property = analyzePropertyChain(node.property, null);
|
1947
|
-
const result =
|
1796
|
+
const result = "".concat(object, ".").concat(property);
|
1948
1797
|
markNode(node, optionalChains, result);
|
1949
1798
|
return result;
|
1950
1799
|
} else if (node.type === 'ChainExpression' && !node.computed) {
|
1951
1800
|
const expression = node.expression;
|
1952
1801
|
if (expression.type === 'CallExpression') {
|
1953
|
-
throw new Error(
|
1802
|
+
throw new Error("Unsupported node type: ".concat(expression.type));
|
1954
1803
|
}
|
1955
1804
|
const object = analyzePropertyChain(expression.object, optionalChains);
|
1956
1805
|
const property = analyzePropertyChain(expression.property, null);
|
1957
|
-
const result =
|
1806
|
+
const result = "".concat(object, ".").concat(property);
|
1958
1807
|
markNode(expression, optionalChains, result);
|
1959
1808
|
return result;
|
1960
1809
|
}
|
1961
|
-
throw new Error(
|
1810
|
+
throw new Error("Unsupported node type: ".concat(node.type));
|
1962
1811
|
}
|
1963
1812
|
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
|
-
) {
|
1813
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'React' && node.property.type === 'Identifier' && !node.computed) {
|
1971
1814
|
return node.property;
|
1972
1815
|
}
|
1973
1816
|
return node;
|
@@ -2046,28 +1889,17 @@ function joinEnglish(arr) {
|
|
2046
1889
|
return s;
|
2047
1890
|
}
|
2048
1891
|
function isNodeLike(val) {
|
2049
|
-
return (
|
2050
|
-
typeof val === 'object' &&
|
2051
|
-
val != null &&
|
2052
|
-
!Array.isArray(val) &&
|
2053
|
-
typeof val.type === 'string'
|
2054
|
-
);
|
1892
|
+
return typeof val === 'object' && val != null && !Array.isArray(val) && typeof val.type === 'string';
|
2055
1893
|
}
|
2056
1894
|
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
|
-
);
|
1895
|
+
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
1896
|
}
|
2065
1897
|
function isAncestorNodeOf(a, b) {
|
2066
1898
|
return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
|
2067
1899
|
}
|
2068
1900
|
|
2069
1901
|
const Components = require('eslint-plugin-react/lib/util/Components');
|
2070
|
-
var
|
1902
|
+
var reactHookUseRef = {
|
2071
1903
|
meta: {
|
2072
1904
|
docs: {
|
2073
1905
|
description: 'Ensure naming of useRef hook value.',
|
@@ -2075,12 +1907,14 @@ var hookUseRef = {
|
|
2075
1907
|
},
|
2076
1908
|
schema: [],
|
2077
1909
|
type: 'suggestion',
|
2078
|
-
hasSuggestions: true
|
1910
|
+
hasSuggestions: true,
|
1911
|
+
messages: {
|
1912
|
+
useRefName: 'useRef call is not end with "Ref"'
|
1913
|
+
}
|
2079
1914
|
},
|
2080
1915
|
create: Components.detect((context, component, util) => ({
|
2081
1916
|
CallExpression(node) {
|
2082
|
-
const isImmediateReturn =
|
2083
|
-
node.parent && node.parent.type === 'ReturnStatement';
|
1917
|
+
const isImmediateReturn = node.parent && node.parent.type === 'ReturnStatement';
|
2084
1918
|
if (isImmediateReturn || !util.isReactHookCall(node, ['useRef'])) {
|
2085
1919
|
return;
|
2086
1920
|
}
|
@@ -2091,7 +1925,7 @@ var hookUseRef = {
|
|
2091
1925
|
if (!variable.endsWith('Ref')) {
|
2092
1926
|
context.report({
|
2093
1927
|
node: node,
|
2094
|
-
|
1928
|
+
messageId: 'useRefName'
|
2095
1929
|
});
|
2096
1930
|
}
|
2097
1931
|
}
|
@@ -2100,54 +1934,39 @@ var hookUseRef = {
|
|
2100
1934
|
|
2101
1935
|
function* updateImportStatement(context, fixer, key) {
|
2102
1936
|
const sourceCode = context.sourceCode;
|
2103
|
-
const importNode = sourceCode.ast.body.find(
|
2104
|
-
node => node.type === 'ImportDeclaration' && node.source.value === 'react'
|
2105
|
-
);
|
1937
|
+
const importNode = sourceCode.ast.body.find(node => node.type === 'ImportDeclaration' && node.source.value === 'react');
|
2106
1938
|
if (!importNode) {
|
2107
|
-
yield fixer.insertTextBefore(
|
2108
|
-
sourceCode.ast.body[0],
|
2109
|
-
`import { ${key} } from 'react';\n`
|
2110
|
-
);
|
1939
|
+
yield fixer.insertTextBefore(sourceCode.ast.body[0], "import { ".concat(key, " } from 'react';\n"));
|
2111
1940
|
return;
|
2112
1941
|
}
|
2113
|
-
if (
|
2114
|
-
importNode.specifiers.
|
2115
|
-
importNode.specifiers[0].type === 'ImportDefaultSpecifier'
|
2116
|
-
) {
|
2117
|
-
yield fixer.insertTextAfter(importNode.specifiers[0], `, { ${key} }`);
|
1942
|
+
if (importNode.specifiers.length === 1 && importNode.specifiers[0].type === 'ImportDefaultSpecifier') {
|
1943
|
+
yield fixer.insertTextAfter(importNode.specifiers[0], ", { ".concat(key, " }"));
|
2118
1944
|
return;
|
2119
1945
|
}
|
2120
|
-
const alreadyImportedKeys = importNode.specifiers
|
2121
|
-
.filter(specifier => specifier.type === 'ImportSpecifier')
|
2122
|
-
.map(specifier => specifier.imported.name);
|
1946
|
+
const alreadyImportedKeys = importNode.specifiers.filter(specifier => specifier.type === 'ImportSpecifier').map(specifier => specifier.imported.name);
|
2123
1947
|
if (alreadyImportedKeys.includes(key)) {
|
2124
1948
|
return;
|
2125
1949
|
}
|
2126
|
-
yield fixer.insertTextAfter([...importNode.specifiers].pop(),
|
1950
|
+
yield fixer.insertTextAfter([...importNode.specifiers].pop(), ", ".concat(key));
|
2127
1951
|
}
|
2128
|
-
var
|
1952
|
+
var reactPreferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
|
2129
1953
|
defaultOptions: [],
|
2130
1954
|
meta: {
|
2131
1955
|
type: 'layout',
|
2132
1956
|
fixable: 'code',
|
2133
1957
|
docs: {
|
2134
|
-
description:
|
2135
|
-
'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
|
1958
|
+
description: 'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
|
2136
1959
|
recommended: 'recommended'
|
2137
1960
|
},
|
2138
1961
|
messages: {
|
2139
|
-
illegalReactPropertyAccess:
|
2140
|
-
'Illegal React property access: {{name}}. Use named import instead.'
|
1962
|
+
illegalReactPropertyAccess: 'Illegal React property access: {{name}}. Use named import instead.'
|
2141
1963
|
},
|
2142
1964
|
schema: []
|
2143
1965
|
},
|
2144
1966
|
create(context) {
|
2145
1967
|
return {
|
2146
1968
|
TSQualifiedName(node) {
|
2147
|
-
if (
|
2148
|
-
('name' in node.left && node.left.name !== 'React') ||
|
2149
|
-
('name' in node.right && node.right.name.endsWith('Event'))
|
2150
|
-
) {
|
1969
|
+
if ('name' in node.left && node.left.name !== 'React' || 'name' in node.right && node.right.name.endsWith('Event')) {
|
2151
1970
|
return;
|
2152
1971
|
}
|
2153
1972
|
context.report({
|
@@ -2182,7 +2001,7 @@ var preferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
2182
2001
|
}
|
2183
2002
|
});
|
2184
2003
|
|
2185
|
-
var
|
2004
|
+
var reactPreferSxProp = {
|
2186
2005
|
meta: {
|
2187
2006
|
docs: {
|
2188
2007
|
description: 'Prefer using sx prop instead of inline styles',
|
@@ -2190,36 +2009,29 @@ var preferSxProp = {
|
|
2190
2009
|
recommended: true
|
2191
2010
|
},
|
2192
2011
|
messages: {
|
2193
|
-
preferSxProp:
|
2194
|
-
'Avoid using inline styles, use sx prop or tss-react or styled-component instead'
|
2012
|
+
preferSxProp: 'Avoid using inline styles, use sx prop or tss-react or styled-component instead'
|
2195
2013
|
},
|
2196
|
-
schema: [
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2201
|
-
|
2202
|
-
|
2203
|
-
|
2014
|
+
schema: [{
|
2015
|
+
type: 'object',
|
2016
|
+
properties: {
|
2017
|
+
allowedFor: {
|
2018
|
+
type: 'array',
|
2019
|
+
uniqueItems: true,
|
2020
|
+
items: {
|
2021
|
+
type: 'string'
|
2204
2022
|
}
|
2205
2023
|
}
|
2206
2024
|
}
|
2207
|
-
]
|
2025
|
+
}]
|
2208
2026
|
},
|
2209
2027
|
create(context) {
|
2210
2028
|
const configuration = context.options[0] || {};
|
2211
2029
|
const allowedFor = configuration.allowedFor || [];
|
2212
2030
|
function checkComponent(node) {
|
2213
2031
|
const parentName = node.parent.name;
|
2214
|
-
const tag =
|
2215
|
-
parentName.name ||
|
2216
|
-
`${parentName.object.name}.${parentName.property.name}`;
|
2032
|
+
const tag = parentName.name || "".concat(parentName.object.name, ".").concat(parentName.property.name);
|
2217
2033
|
const componentName = parentName.name || parentName.property.name;
|
2218
|
-
if (
|
2219
|
-
componentName &&
|
2220
|
-
typeof componentName[0] === 'string' &&
|
2221
|
-
componentName[0] !== componentName[0].toUpperCase()
|
2222
|
-
) {
|
2034
|
+
if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) {
|
2223
2035
|
return;
|
2224
2036
|
}
|
2225
2037
|
if (allowedFor.includes(tag)) {
|
@@ -2235,9 +2047,7 @@ var preferSxProp = {
|
|
2235
2047
|
}
|
2236
2048
|
function checkDOMNodes(node) {
|
2237
2049
|
const tag = node.parent.name.name;
|
2238
|
-
if (
|
2239
|
-
!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())
|
2240
|
-
) {
|
2050
|
+
if (!(tag && typeof tag === 'string' && tag[0] !== tag[0].toUpperCase())) {
|
2241
2051
|
return;
|
2242
2052
|
}
|
2243
2053
|
if (allowedFor.includes(tag)) {
|
@@ -2277,25 +2087,24 @@ function getBasicIdentifier(node) {
|
|
2277
2087
|
}
|
2278
2088
|
function getBaseIdentifier(node) {
|
2279
2089
|
switch (node.type) {
|
2280
|
-
case 'Identifier':
|
2281
|
-
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
|
2287
|
-
|
2288
|
-
|
2090
|
+
case 'Identifier':
|
2091
|
+
{
|
2092
|
+
return node;
|
2093
|
+
}
|
2094
|
+
case 'CallExpression':
|
2095
|
+
{
|
2096
|
+
return getBaseIdentifier(node.callee);
|
2097
|
+
}
|
2098
|
+
case 'MemberExpression':
|
2099
|
+
{
|
2100
|
+
return getBaseIdentifier(node.object);
|
2101
|
+
}
|
2289
2102
|
}
|
2290
2103
|
return null;
|
2291
2104
|
}
|
2292
2105
|
function getStyesObj(node) {
|
2293
2106
|
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';
|
2107
|
+
const isModernApi = node.callee.type === 'MemberExpression' && node.callee.property.name === 'create' && getBaseIdentifier(node.callee.object) && getBaseIdentifier(node.callee.object).name === 'tss';
|
2299
2108
|
if (!isMakeStyles && !isModernApi) {
|
2300
2109
|
return;
|
2301
2110
|
}
|
@@ -2315,35 +2124,51 @@ function getStyesObj(node) {
|
|
2315
2124
|
return styles;
|
2316
2125
|
case 'ArrowFunctionExpression':
|
2317
2126
|
{
|
2318
|
-
const {
|
2127
|
+
const {
|
2128
|
+
body
|
2129
|
+
} = styles;
|
2319
2130
|
switch (body.type) {
|
2320
2131
|
case 'ObjectExpression':
|
2321
2132
|
return body;
|
2322
|
-
case 'BlockStatement':
|
2323
|
-
|
2324
|
-
|
2325
|
-
|
2326
|
-
bodyNode.type === 'ReturnStatement' &&
|
2327
|
-
|
2328
|
-
|
2329
|
-
|
2330
|
-
|
2331
|
-
}
|
2332
|
-
return stylesObj;
|
2333
|
-
}
|
2133
|
+
case 'BlockStatement':
|
2134
|
+
{
|
2135
|
+
let stylesObj;
|
2136
|
+
body.body.forEach(bodyNode => {
|
2137
|
+
if (bodyNode.type === 'ReturnStatement' && bodyNode.argument.type === 'ObjectExpression') {
|
2138
|
+
stylesObj = bodyNode.argument;
|
2139
|
+
}
|
2140
|
+
});
|
2141
|
+
return stylesObj;
|
2142
|
+
}
|
2334
2143
|
}
|
2335
2144
|
}
|
2336
2145
|
break;
|
2337
2146
|
}
|
2338
2147
|
}
|
2148
|
+
function loopStylesObj(node, callback) {
|
2149
|
+
if (node && node.type === 'ObjectExpression') {
|
2150
|
+
node.properties.forEach(property => {
|
2151
|
+
if (property.type === 'Property' && property.value) {
|
2152
|
+
if (property.value.type === 'ObjectExpression') {
|
2153
|
+
loopStylesObj(property.value, callback);
|
2154
|
+
} else {
|
2155
|
+
callback(property.value);
|
2156
|
+
}
|
2157
|
+
}
|
2158
|
+
});
|
2159
|
+
}
|
2160
|
+
}
|
2339
2161
|
|
2340
|
-
var
|
2162
|
+
var tssClassNaming = {
|
2341
2163
|
meta: {
|
2342
2164
|
type: 'problem',
|
2343
2165
|
docs: {
|
2344
2166
|
description: 'Enforce camelCase class names in TSS',
|
2345
2167
|
category: 'Best Practices',
|
2346
2168
|
recommended: true
|
2169
|
+
},
|
2170
|
+
messages: {
|
2171
|
+
camelCase: 'Class `{{ className }}` must be camelCase in TSS.'
|
2347
2172
|
}
|
2348
2173
|
},
|
2349
2174
|
create: function rule(context) {
|
@@ -2357,17 +2182,17 @@ var classNaming = {
|
|
2357
2182
|
if (property.computed) {
|
2358
2183
|
return;
|
2359
2184
|
}
|
2360
|
-
if (
|
2361
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
2362
|
-
property.type === 'SpreadElement'
|
2363
|
-
) {
|
2185
|
+
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
|
2364
2186
|
return;
|
2365
2187
|
}
|
2366
2188
|
const className = property.key.value || property.key.name;
|
2367
2189
|
if (!eslintUtils.isCamelCase(className)) {
|
2368
2190
|
context.report({
|
2369
2191
|
node: property,
|
2370
|
-
|
2192
|
+
messageId: 'camelCase',
|
2193
|
+
data: {
|
2194
|
+
className
|
2195
|
+
}
|
2371
2196
|
});
|
2372
2197
|
}
|
2373
2198
|
});
|
@@ -2376,20 +2201,201 @@ var classNaming = {
|
|
2376
2201
|
}
|
2377
2202
|
};
|
2378
2203
|
|
2379
|
-
var
|
2204
|
+
var colors = {
|
2205
|
+
aliceblue: [240, 248, 255],
|
2206
|
+
antiquewhite: [250, 235, 215],
|
2207
|
+
aqua: [0, 255, 255],
|
2208
|
+
aquamarine: [127, 255, 212],
|
2209
|
+
azure: [240, 255, 255],
|
2210
|
+
beige: [245, 245, 220],
|
2211
|
+
bisque: [255, 228, 196],
|
2212
|
+
black: [0, 0, 0],
|
2213
|
+
blanchedalmond: [255, 235, 205],
|
2214
|
+
blue: [0, 0, 255],
|
2215
|
+
blueviolet: [138, 43, 226],
|
2216
|
+
brown: [165, 42, 42],
|
2217
|
+
burlywood: [222, 184, 135],
|
2218
|
+
cadetblue: [95, 158, 160],
|
2219
|
+
chartreuse: [127, 255, 0],
|
2220
|
+
chocolate: [210, 105, 30],
|
2221
|
+
coral: [255, 127, 80],
|
2222
|
+
cornflowerblue: [100, 149, 237],
|
2223
|
+
cornsilk: [255, 248, 220],
|
2224
|
+
crimson: [220, 20, 60],
|
2225
|
+
cyan: [0, 255, 255],
|
2226
|
+
darkblue: [0, 0, 139],
|
2227
|
+
darkcyan: [0, 139, 139],
|
2228
|
+
darkgoldenrod: [184, 134, 11],
|
2229
|
+
darkgray: [169, 169, 169],
|
2230
|
+
darkgreen: [0, 100, 0],
|
2231
|
+
darkgrey: [169, 169, 169],
|
2232
|
+
darkkhaki: [189, 183, 107],
|
2233
|
+
darkmagenta: [139, 0, 139],
|
2234
|
+
darkolivegreen: [85, 107, 47],
|
2235
|
+
darkorange: [255, 140, 0],
|
2236
|
+
darkorchid: [153, 50, 204],
|
2237
|
+
darkred: [139, 0, 0],
|
2238
|
+
darksalmon: [233, 150, 122],
|
2239
|
+
darkseagreen: [143, 188, 143],
|
2240
|
+
darkslateblue: [72, 61, 139],
|
2241
|
+
darkslategray: [47, 79, 79],
|
2242
|
+
darkslategrey: [47, 79, 79],
|
2243
|
+
darkturquoise: [0, 206, 209],
|
2244
|
+
darkviolet: [148, 0, 211],
|
2245
|
+
deeppink: [255, 20, 147],
|
2246
|
+
deepskyblue: [0, 191, 255],
|
2247
|
+
dimgray: [105, 105, 105],
|
2248
|
+
dimgrey: [105, 105, 105],
|
2249
|
+
dodgerblue: [30, 144, 255],
|
2250
|
+
firebrick: [178, 34, 34],
|
2251
|
+
floralwhite: [255, 250, 240],
|
2252
|
+
forestgreen: [34, 139, 34],
|
2253
|
+
fuchsia: [255, 0, 255],
|
2254
|
+
gainsboro: [220, 220, 220],
|
2255
|
+
ghostwhite: [248, 248, 255],
|
2256
|
+
gold: [255, 215, 0],
|
2257
|
+
goldenrod: [218, 165, 32],
|
2258
|
+
gray: [128, 128, 128],
|
2259
|
+
green: [0, 128, 0],
|
2260
|
+
greenyellow: [173, 255, 47],
|
2261
|
+
grey: [128, 128, 128],
|
2262
|
+
honeydew: [240, 255, 240],
|
2263
|
+
hotpink: [255, 105, 180],
|
2264
|
+
indianred: [205, 92, 92],
|
2265
|
+
indigo: [75, 0, 130],
|
2266
|
+
ivory: [255, 255, 240],
|
2267
|
+
khaki: [240, 230, 140],
|
2268
|
+
lavender: [230, 230, 250],
|
2269
|
+
lavenderblush: [255, 240, 245],
|
2270
|
+
lawngreen: [124, 252, 0],
|
2271
|
+
lemonchiffon: [255, 250, 205],
|
2272
|
+
lightblue: [173, 216, 230],
|
2273
|
+
lightcoral: [240, 128, 128],
|
2274
|
+
lightcyan: [224, 255, 255],
|
2275
|
+
lightgoldenrodyellow: [250, 250, 210],
|
2276
|
+
lightgray: [211, 211, 211],
|
2277
|
+
lightgreen: [144, 238, 144],
|
2278
|
+
lightgrey: [211, 211, 211],
|
2279
|
+
lightpink: [255, 182, 193],
|
2280
|
+
lightsalmon: [255, 160, 122],
|
2281
|
+
lightseagreen: [32, 178, 170],
|
2282
|
+
lightskyblue: [135, 206, 250],
|
2283
|
+
lightslategray: [119, 136, 153],
|
2284
|
+
lightslategrey: [119, 136, 153],
|
2285
|
+
lightsteelblue: [176, 196, 222],
|
2286
|
+
lightyellow: [255, 255, 224],
|
2287
|
+
lime: [0, 255, 0],
|
2288
|
+
limegreen: [50, 205, 50],
|
2289
|
+
linen: [250, 240, 230],
|
2290
|
+
magenta: [255, 0, 255],
|
2291
|
+
maroon: [128, 0, 0],
|
2292
|
+
mediumaquamarine: [102, 205, 170],
|
2293
|
+
mediumblue: [0, 0, 205],
|
2294
|
+
mediumorchid: [186, 85, 211],
|
2295
|
+
mediumpurple: [147, 112, 219],
|
2296
|
+
mediumseagreen: [60, 179, 113],
|
2297
|
+
mediumslateblue: [123, 104, 238],
|
2298
|
+
mediumspringgreen: [0, 250, 154],
|
2299
|
+
mediumturquoise: [72, 209, 204],
|
2300
|
+
mediumvioletred: [199, 21, 133],
|
2301
|
+
midnightblue: [25, 25, 112],
|
2302
|
+
mintcream: [245, 255, 250],
|
2303
|
+
mistyrose: [255, 228, 225],
|
2304
|
+
moccasin: [255, 228, 181],
|
2305
|
+
navajowhite: [255, 222, 173],
|
2306
|
+
navy: [0, 0, 128],
|
2307
|
+
oldlace: [253, 245, 230],
|
2308
|
+
olive: [128, 128, 0],
|
2309
|
+
olivedrab: [107, 142, 35],
|
2310
|
+
orange: [255, 165, 0],
|
2311
|
+
orangered: [255, 69, 0],
|
2312
|
+
orchid: [218, 112, 214],
|
2313
|
+
palegoldenrod: [238, 232, 170],
|
2314
|
+
palegreen: [152, 251, 152],
|
2315
|
+
paleturquoise: [175, 238, 238],
|
2316
|
+
palevioletred: [219, 112, 147],
|
2317
|
+
papayawhip: [255, 239, 213],
|
2318
|
+
peachpuff: [255, 218, 185],
|
2319
|
+
peru: [205, 133, 63],
|
2320
|
+
pink: [255, 192, 203],
|
2321
|
+
plum: [221, 160, 221],
|
2322
|
+
powderblue: [176, 224, 230],
|
2323
|
+
purple: [128, 0, 128],
|
2324
|
+
rebeccapurple: [102, 51, 153],
|
2325
|
+
red: [255, 0, 0],
|
2326
|
+
rosybrown: [188, 143, 143],
|
2327
|
+
royalblue: [65, 105, 225],
|
2328
|
+
saddlebrown: [139, 69, 19],
|
2329
|
+
salmon: [250, 128, 114],
|
2330
|
+
sandybrown: [244, 164, 96],
|
2331
|
+
seagreen: [46, 139, 87],
|
2332
|
+
seashell: [255, 245, 238],
|
2333
|
+
sienna: [160, 82, 45],
|
2334
|
+
silver: [192, 192, 192],
|
2335
|
+
skyblue: [135, 206, 235],
|
2336
|
+
slateblue: [106, 90, 205],
|
2337
|
+
slategray: [112, 128, 144],
|
2338
|
+
slategrey: [112, 128, 144],
|
2339
|
+
snow: [255, 250, 250],
|
2340
|
+
springgreen: [0, 255, 127],
|
2341
|
+
steelblue: [70, 130, 180],
|
2342
|
+
tan: [210, 180, 140],
|
2343
|
+
teal: [0, 128, 128],
|
2344
|
+
thistle: [216, 191, 216],
|
2345
|
+
tomato: [255, 99, 71],
|
2346
|
+
turquoise: [64, 224, 208],
|
2347
|
+
violet: [238, 130, 238],
|
2348
|
+
wheat: [245, 222, 179],
|
2349
|
+
white: [255, 255, 255],
|
2350
|
+
whitesmoke: [245, 245, 245],
|
2351
|
+
yellow: [255, 255, 0],
|
2352
|
+
yellowgreen: [154, 205, 50]
|
2353
|
+
};
|
2354
|
+
|
2355
|
+
var tssNoColorName = {
|
2380
2356
|
meta: {
|
2381
|
-
type: '
|
2357
|
+
type: 'suggestion',
|
2382
2358
|
docs: {
|
2383
|
-
description:
|
2384
|
-
'Enforce the use of color variables instead of color codes within TSS',
|
2359
|
+
description: 'Enforce the use of color variables instead of color name within TSS',
|
2385
2360
|
recommended: true
|
2361
|
+
},
|
2362
|
+
messages: {
|
2363
|
+
disallowColorName: 'Disallowed color name. Use color from `@mui/material/colors` or `theme.palette`.'
|
2386
2364
|
}
|
2387
2365
|
},
|
2388
2366
|
create: function (context) {
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2367
|
+
return {
|
2368
|
+
CallExpression(node) {
|
2369
|
+
const stylesObj = getStyesObj(node);
|
2370
|
+
if (!stylesObj) {
|
2371
|
+
return;
|
2372
|
+
}
|
2373
|
+
function checkColorLiteral(value) {
|
2374
|
+
if (value.type === 'Literal' && typeof value.value === 'string' && Object.keys(colors).includes(value.value.toLowerCase())) {
|
2375
|
+
context.report({
|
2376
|
+
node: value,
|
2377
|
+
messageId: 'disallowColorName'
|
2378
|
+
});
|
2379
|
+
}
|
2380
|
+
}
|
2381
|
+
loopStylesObj(stylesObj, checkColorLiteral);
|
2382
|
+
}
|
2383
|
+
};
|
2384
|
+
}
|
2385
|
+
};
|
2386
|
+
|
2387
|
+
var tssNoColorValue = {
|
2388
|
+
meta: {
|
2389
|
+
type: 'problem',
|
2390
|
+
docs: {
|
2391
|
+
description: 'Enforce the use of color variables instead of color codes within TSS',
|
2392
|
+
recommended: true
|
2393
|
+
},
|
2394
|
+
messages: {
|
2395
|
+
preferMuiColor: 'Prefer use color from `@mui/material/colors` or `theme.palette`.'
|
2392
2396
|
}
|
2397
|
+
},
|
2398
|
+
create: function (context) {
|
2393
2399
|
return {
|
2394
2400
|
CallExpression(node) {
|
2395
2401
|
const stylesObj = getStyesObj(node);
|
@@ -2398,43 +2404,31 @@ var noColorValue = {
|
|
2398
2404
|
}
|
2399
2405
|
function checkColorLiteral(value) {
|
2400
2406
|
if (value.type === 'Literal' && typeof value.value === 'string') {
|
2401
|
-
const colorCodePattern =
|
2402
|
-
|
2403
|
-
const isColorCode = colorCodePattern.test(value.value);
|
2404
|
-
if (isColorCode) {
|
2407
|
+
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;
|
2408
|
+
if (colorCodePattern.test(value.value)) {
|
2405
2409
|
context.report({
|
2406
2410
|
node: value,
|
2407
|
-
|
2411
|
+
messageId: 'preferMuiColor'
|
2408
2412
|
});
|
2409
2413
|
}
|
2410
2414
|
}
|
2411
2415
|
}
|
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);
|
2416
|
+
loopStylesObj(stylesObj, checkColorLiteral);
|
2426
2417
|
}
|
2427
2418
|
};
|
2428
2419
|
}
|
2429
2420
|
};
|
2430
2421
|
|
2431
|
-
var
|
2422
|
+
var tssUnusedClasses = {
|
2432
2423
|
meta: {
|
2433
2424
|
type: 'suggestion',
|
2434
2425
|
docs: {
|
2435
2426
|
description: 'Disallow unused classes in tss',
|
2436
2427
|
category: 'Best Practices',
|
2437
2428
|
recommended: true
|
2429
|
+
},
|
2430
|
+
messages: {
|
2431
|
+
unusedClass: 'Class `{{ className }}` is unused in JSX'
|
2438
2432
|
}
|
2439
2433
|
},
|
2440
2434
|
create: function rule(context) {
|
@@ -2450,20 +2444,14 @@ var unusedClasses = {
|
|
2450
2444
|
if (property.computed) {
|
2451
2445
|
return;
|
2452
2446
|
}
|
2453
|
-
if (
|
2454
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
2455
|
-
property.type === 'SpreadElement'
|
2456
|
-
) {
|
2447
|
+
if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') {
|
2457
2448
|
return;
|
2458
2449
|
}
|
2459
2450
|
definedClasses[property.key.value || property.key.name] = property;
|
2460
2451
|
});
|
2461
2452
|
},
|
2462
2453
|
MemberExpression(node) {
|
2463
|
-
if (
|
2464
|
-
node.object.type === 'Identifier' &&
|
2465
|
-
node.object.name === 'classes'
|
2466
|
-
) {
|
2454
|
+
if (node.object.type === 'Identifier' && node.object.name === 'classes') {
|
2467
2455
|
const whichClass = getBasicIdentifier(node.property);
|
2468
2456
|
if (whichClass) {
|
2469
2457
|
usedClasses[whichClass] = true;
|
@@ -2477,14 +2465,15 @@ var unusedClasses = {
|
|
2477
2465
|
if (classIdentifier !== 'classes') {
|
2478
2466
|
return;
|
2479
2467
|
}
|
2480
|
-
const {
|
2468
|
+
const {
|
2469
|
+
parent
|
2470
|
+
} = node;
|
2481
2471
|
if (parent.type !== 'MemberExpression') {
|
2482
2472
|
return;
|
2483
2473
|
}
|
2484
2474
|
if (
|
2485
|
-
|
2486
|
-
|
2487
|
-
) {
|
2475
|
+
node.object.object &&
|
2476
|
+
node.object.object.type !== 'ThisExpression') {
|
2488
2477
|
return;
|
2489
2478
|
}
|
2490
2479
|
const propsIdentifier = getBasicIdentifier(parent.object);
|
@@ -2507,7 +2496,10 @@ var unusedClasses = {
|
|
2507
2496
|
if (!usedClasses[definedClassKey]) {
|
2508
2497
|
context.report({
|
2509
2498
|
node: definedClasses[definedClassKey],
|
2510
|
-
|
2499
|
+
messageId: 'unusedClass',
|
2500
|
+
data: {
|
2501
|
+
className: definedClassKey
|
2502
|
+
}
|
2511
2503
|
});
|
2512
2504
|
}
|
2513
2505
|
});
|
@@ -2516,177 +2508,27 @@ var unusedClasses = {
|
|
2516
2508
|
}
|
2517
2509
|
};
|
2518
2510
|
|
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
2511
|
var ruleFiles = /*#__PURE__*/Object.freeze({
|
2673
2512
|
__proto__: null,
|
2674
|
-
|
2675
|
-
rules_import_monorepo:
|
2676
|
-
rules_intl_id_missing:
|
2677
|
-
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2684
|
-
|
2685
|
-
|
2686
|
-
|
2687
|
-
|
2688
|
-
|
2689
|
-
|
2513
|
+
rules_enforce_mui_icon_alias: enforceMuiIconAlias,
|
2514
|
+
rules_import_monorepo: importMonorepo,
|
2515
|
+
rules_intl_id_missing: intlIdMissing,
|
2516
|
+
rules_intl_id_naming: intlIdNaming,
|
2517
|
+
rules_intl_id_prefix: intlIdPrefix,
|
2518
|
+
rules_intl_id_unused: intlIdUnused,
|
2519
|
+
rules_intl_no_default: intlNoDefault,
|
2520
|
+
rules_no_async_array_methods: noAsyncArrayMethods,
|
2521
|
+
rules_no_import_css: noImportCss,
|
2522
|
+
rules_no_then_catch_finally: noThenCatchFinally,
|
2523
|
+
rules_no_unnecessary_template_literals: noUnnecessaryTemplateLiterals,
|
2524
|
+
rules_react_better_exhaustive_deps: reactBetterExhaustiveDeps,
|
2525
|
+
rules_react_hook_use_ref: reactHookUseRef,
|
2526
|
+
rules_react_prefer_named_property_access: reactPreferNamedPropertyAccess,
|
2527
|
+
rules_react_prefer_sx_prop: reactPreferSxProp,
|
2528
|
+
rules_tss_class_naming: tssClassNaming,
|
2529
|
+
rules_tss_no_color_name: tssNoColorName,
|
2530
|
+
rules_tss_no_color_value: tssNoColorValue,
|
2531
|
+
rules_tss_unused_classes: tssUnusedClasses
|
2690
2532
|
});
|
2691
2533
|
|
2692
2534
|
const plugin = {
|
@@ -2704,13 +2546,12 @@ const plugin = {
|
|
2704
2546
|
}
|
2705
2547
|
};
|
2706
2548
|
Object.keys(ruleFiles).forEach(key => {
|
2707
|
-
const ruleKey = key.replace(/^rules_/, '')
|
2549
|
+
const ruleKey = key.replace(/^rules_/, '');
|
2708
2550
|
const finalKey = ruleKey.replace(/_/g, '-');
|
2709
2551
|
const rule = ruleFiles[key];
|
2710
2552
|
plugin.rules[finalKey] = rule;
|
2711
2553
|
if (rule.meta && rule.meta.docs && rule.meta.docs.recommended) {
|
2712
|
-
plugin.configs.recommended.rules[
|
2713
|
-
rule.meta.type === 'suggestion' ? 'warn' : 'error';
|
2554
|
+
plugin.configs.recommended.rules["@agilebot/".concat(finalKey)] = rule.meta.type === 'suggestion' ? 'warn' : 'error';
|
2714
2555
|
}
|
2715
2556
|
});
|
2716
2557
|
|