feat(eslint-plugin-react-internal): support ESLint 8.x (#22249)

Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
Michaël De Boey
2021-09-06 21:42:40 +02:00
committed by GitHub
parent 6f64eb5aa0
commit b394c38e88
14 changed files with 227 additions and 208 deletions

View File

@@ -183,7 +183,10 @@ module.exports = {
},
},
{
files: ['packages/eslint-plugin-react-hooks/src/*.js'],
files: [
'scripts/eslint-rules/*.js',
'packages/eslint-plugin-react-hooks/src/*.js'
],
plugins: ['eslint-plugin'],
rules: {
'eslint-plugin/prefer-object-rule': ERROR,

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../invariant-args');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester();
ruleTester.run('eslint-rules/invariant-args', rule, {

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../no-cross-fork-imports');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 8,

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../no-cross-fork-types');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 8,

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../no-primitive-constructors');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester();
ruleTester.run('eslint-rules/no-primitive-constructors', rule, {

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../no-production-logging');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester();
ruleTester.run('no-production-logging', rule, {

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../no-to-warn-dev-within-to-throw');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester();
ruleTester.run('eslint-rules/no-to-warn-dev-within-to-throw', rule, {

View File

@@ -10,7 +10,7 @@
'use strict';
const rule = require('../warning-args');
const RuleTester = require('eslint').RuleTester;
const {RuleTester} = require('eslint');
const ruleTester = new RuleTester();
ruleTester.run('eslint-rules/warning-args', rule, {

View File

@@ -24,83 +24,86 @@ Object.keys(existingErrorMap).forEach(key =>
* argument.
*/
module.exports = function(context) {
// we also allow literal strings and concatenated literal strings
function getLiteralString(node) {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
} else if (node.type === 'BinaryExpression' && node.operator === '+') {
const l = getLiteralString(node.left);
const r = getLiteralString(node.right);
if (l !== null && r !== null) {
return l + r;
module.exports = {
meta: {
schema: [],
},
create(context) {
// we also allow literal strings and concatenated literal strings
function getLiteralString(node) {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
} else if (node.type === 'BinaryExpression' && node.operator === '+') {
const l = getLiteralString(node.left);
const r = getLiteralString(node.right);
if (l !== null && r !== null) {
return l + r;
}
}
return null;
}
return null;
}
return {
CallExpression: function(node) {
// This could be a little smarter by checking context.getScope() to see
// how warning/invariant was defined.
const isInvariant =
node.callee.type === 'Identifier' && node.callee.name === 'invariant';
if (!isInvariant) {
return;
}
if (node.arguments.length < 2) {
context.report(node, '{{name}} takes at least two arguments', {
name: node.callee.name,
});
return;
}
const format = getLiteralString(node.arguments[1]);
if (format === null) {
context.report(
node,
'The second argument to {{name}} must be a string literal',
{name: node.callee.name}
);
return;
}
if (format.length < 10 || /^[s\W]*$/.test(format)) {
context.report(
node,
'The {{name}} format should be able to uniquely identify this ' +
'{{name}}. Please, use a more descriptive format than: {{format}}',
{name: node.callee.name, format: format}
);
return;
}
// count the number of formatting substitutions, plus the first two args
const expectedNArgs = (format.match(/%s/g) || []).length + 2;
if (node.arguments.length !== expectedNArgs) {
context.report(
node,
'Expected {{expectedNArgs}} arguments in call to {{name}} based on ' +
'the number of "%s" substitutions, but got {{length}}',
{
expectedNArgs: expectedNArgs,
return {
CallExpression: function(node) {
// This could be a little smarter by checking context.getScope() to see
// how warning/invariant was defined.
const isInvariant =
node.callee.type === 'Identifier' && node.callee.name === 'invariant';
if (!isInvariant) {
return;
}
if (node.arguments.length < 2) {
context.report(node, '{{name}} takes at least two arguments', {
name: node.callee.name,
length: node.arguments.length,
}
);
}
});
return;
}
const format = getLiteralString(node.arguments[1]);
if (format === null) {
context.report(
node,
'The second argument to {{name}} must be a string literal',
{name: node.callee.name}
);
return;
}
if (format.length < 10 || /^[s\W]*$/.test(format)) {
context.report(
node,
'The {{name}} format should be able to uniquely identify this ' +
'{{name}}. Please, use a more descriptive format than: {{format}}',
{name: node.callee.name, format: format}
);
return;
}
// count the number of formatting substitutions, plus the first two args
const expectedNArgs = (format.match(/%s/g) || []).length + 2;
if (node.arguments.length !== expectedNArgs) {
context.report(
node,
'Expected {{expectedNArgs}} arguments in call to {{name}} based on ' +
'the number of "%s" substitutions, but got {{length}}',
{
expectedNArgs: expectedNArgs,
name: node.callee.name,
length: node.arguments.length,
}
);
}
if (!messages.has(format)) {
context.report(
node,
'Error message does not have a corresponding production ' +
'error code.\n\n' +
'Run `yarn extract-errors` to add the message to error code ' +
'map, so it can be stripped from the production builds. ' +
"Alternatively, if you're updating an existing error " +
'message, you can modify ' +
'`scripts/error-codes/codes.json` directly.'
);
}
},
};
if (!messages.has(format)) {
context.report(
node,
'Error message does not have a corresponding production ' +
'error code.\n\n' +
'Run `yarn extract-errors` to add the message to error code ' +
'map, so it can be stripped from the production builds. ' +
"Alternatively, if you're updating an existing error " +
'message, you can modify ' +
'`scripts/error-codes/codes.json` directly.'
);
}
},
};
},
};
module.exports.schema = [];

View File

@@ -46,7 +46,6 @@ function warnIfOldField(context, oldFields, identifier) {
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
},
create(context) {
const sourceFilename = context.getFilename();

View File

@@ -9,42 +9,47 @@
'use strict';
module.exports = function(context) {
function report(node, name, msg) {
context.report(node, `Do not use the ${name} constructor. ${msg}`);
}
function check(node) {
const name = node.callee.name;
switch (name) {
case 'Boolean':
report(
node,
name,
'To cast a value to a boolean, use double negation: !!value'
);
break;
case 'String':
report(
node,
name,
'To cast a value to a string, concat it with the empty string ' +
"(unless it's a symbol, which has different semantics): " +
"'' + value"
);
break;
case 'Number':
report(
node,
name,
'To cast a value to a number, use the plus operator: +value'
);
break;
module.exports = {
meta: {
schema: [],
},
create(context) {
function report(node, name, msg) {
context.report(node, `Do not use the ${name} constructor. ${msg}`);
}
}
return {
CallExpression: check,
NewExpression: check,
};
function check(node) {
const name = node.callee.name;
switch (name) {
case 'Boolean':
report(
node,
name,
'To cast a value to a boolean, use double negation: !!value'
);
break;
case 'String':
report(
node,
name,
'To cast a value to a string, concat it with the empty string ' +
"(unless it's a symbol, which has different semantics): " +
"'' + value"
);
break;
case 'Number':
report(
node,
name,
'To cast a value to a number, use the plus operator: +value'
);
break;
}
}
return {
CallExpression: check,
NewExpression: check,
};
},
};

View File

@@ -12,6 +12,7 @@
module.exports = {
meta: {
fixable: 'code',
schema: [],
},
create: function(context) {
function isInDEVBlock(node) {

View File

@@ -9,28 +9,33 @@
'use strict';
module.exports = function(context) {
return {
Identifier(node) {
if (node.name === 'toWarnDev' || node.name === 'toErrorDev') {
let current = node;
while (current.parent) {
if (current.type === 'CallExpression') {
if (
current &&
current.callee &&
current.callee.property &&
current.callee.property.name === 'toThrow'
) {
context.report(
node,
node.name + '() matcher should not be nested'
);
module.exports = {
meta: {
schema: [],
},
create(context) {
return {
Identifier(node) {
if (node.name === 'toWarnDev' || node.name === 'toErrorDev') {
let current = node;
while (current.parent) {
if (current.type === 'CallExpression') {
if (
current &&
current.callee &&
current.callee.property &&
current.callee.property.name === 'toThrow'
) {
context.report(
node,
node.name + '() matcher should not be nested'
);
}
}
current = current.parent;
}
current = current.parent;
}
}
},
};
},
};
},
};

View File

@@ -24,76 +24,79 @@ Object.keys(existingErrorMap).forEach(key =>
* argument.
*/
module.exports = function(context) {
// we also allow literal strings and concatenated literal strings
function getLiteralString(node) {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
} else if (node.type === 'BinaryExpression' && node.operator === '+') {
const l = getLiteralString(node.left);
const r = getLiteralString(node.right);
if (l !== null && r !== null) {
return l + r;
module.exports = {
meta: {
schema: [],
},
create(context) {
// we also allow literal strings and concatenated literal strings
function getLiteralString(node) {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
} else if (node.type === 'BinaryExpression' && node.operator === '+') {
const l = getLiteralString(node.left);
const r = getLiteralString(node.right);
if (l !== null && r !== null) {
return l + r;
}
}
return null;
}
return null;
}
return {
CallExpression: function(node) {
// This could be a little smarter by checking context.getScope() to see
// how warning/invariant was defined.
const isWarning =
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'console' &&
node.callee.property.type === 'Identifier' &&
(node.callee.property.name === 'error' ||
node.callee.property.name === 'warn');
if (!isWarning) {
return;
}
const name = 'console.' + node.callee.property.name;
if (node.arguments.length < 1) {
context.report(node, '{{name}} takes at least one argument', {
name,
});
return;
}
const format = getLiteralString(node.arguments[0]);
if (format === null) {
context.report(
node,
'The first argument to {{name}} must be a string literal',
{name}
);
return;
}
if (format.length < 10 || /^[s\W]*$/.test(format)) {
context.report(
node,
'The {{name}} format should be able to uniquely identify this ' +
'warning. Please, use a more descriptive format than: {{format}}',
{name, format}
);
return;
}
// count the number of formatting substitutions, plus the first two args
const expectedNArgs = (format.match(/%s/g) || []).length + 1;
if (node.arguments.length !== expectedNArgs) {
context.report(
node,
'Expected {{expectedNArgs}} arguments in call to {{name}} based on ' +
'the number of "%s" substitutions, but got {{length}}',
{
expectedNArgs: expectedNArgs,
return {
CallExpression: function(node) {
// This could be a little smarter by checking context.getScope() to see
// how warning/invariant was defined.
const isWarning =
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'console' &&
node.callee.property.type === 'Identifier' &&
(node.callee.property.name === 'error' ||
node.callee.property.name === 'warn');
if (!isWarning) {
return;
}
const name = 'console.' + node.callee.property.name;
if (node.arguments.length < 1) {
context.report(node, '{{name}} takes at least one argument', {
name,
length: node.arguments.length,
}
);
}
},
};
});
return;
}
const format = getLiteralString(node.arguments[0]);
if (format === null) {
context.report(
node,
'The first argument to {{name}} must be a string literal',
{name}
);
return;
}
if (format.length < 10 || /^[s\W]*$/.test(format)) {
context.report(
node,
'The {{name}} format should be able to uniquely identify this ' +
'warning. Please, use a more descriptive format than: {{format}}',
{name, format}
);
return;
}
// count the number of formatting substitutions, plus the first two args
const expectedNArgs = (format.match(/%s/g) || []).length + 1;
if (node.arguments.length !== expectedNArgs) {
context.report(
node,
'Expected {{expectedNArgs}} arguments in call to {{name}} based on ' +
'the number of "%s" substitutions, but got {{length}}',
{
expectedNArgs: expectedNArgs,
name,
length: node.arguments.length,
}
);
}
},
};
},
};
module.exports.schema = [];