tools: prepare custom rules for ESLint v9

Refs: https://eslint.org/docs/latest/use/migrate-to-9.0.0
PR-URL: https://github.com/nodejs/node/pull/52889
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
This commit is contained in:
Michaël Zasso 2024-05-09 17:27:39 +02:00 committed by GitHub
parent 2b657ccfb3
commit 3c1069bb06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 142 additions and 118 deletions

View file

@ -56,7 +56,7 @@ module.exports = {
schema: [],
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
return {
ArrayPattern(node) {

View file

@ -25,41 +25,43 @@ function isTopLevel(node) {
return false;
}
module.exports = (context) => {
if (context.parserOptions.sourceType === 'module') {
return {};
}
function getRequiredModuleNameFromCall(node) {
// Node has arguments and first argument is string
if (node.arguments.length && isString(node.arguments[0])) {
return node.arguments[0].value.trim();
module.exports = {
create(context) {
if (context.parserOptions.sourceType === 'module') {
return {};
}
return undefined;
}
const required = new Set();
const rules = {
CallExpression: (node) => {
if (isRequireCall(node) && isTopLevel(node)) {
const moduleName = getRequiredModuleNameFromCall(node);
if (moduleName === undefined) {
return;
}
if (required.has(moduleName)) {
context.report(
node,
'\'{{moduleName}}\' require is duplicated.',
{ moduleName },
);
} else {
required.add(moduleName);
}
function getRequiredModuleNameFromCall(node) {
// Node has arguments and first argument is string
if (node.arguments.length && isString(node.arguments[0])) {
return node.arguments[0].value.trim();
}
},
};
return rules;
return undefined;
}
const required = new Set();
const rules = {
CallExpression: (node) => {
if (isRequireCall(node) && isTopLevel(node)) {
const moduleName = getRequiredModuleNameFromCall(node);
if (moduleName === undefined) {
return;
}
if (required.has(moduleName)) {
context.report(
node,
'\'{{moduleName}}\' require is duplicated.',
{ moduleName },
);
} else {
required.add(moduleName);
}
}
},
};
return rules;
},
};

View file

@ -10,7 +10,7 @@
module.exports = {
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const regexpStack = [];
let regexpBuffer = [];
let inRegExp = false;

View file

@ -24,32 +24,34 @@ const suggestions = {
'—': '-',
};
module.exports = (context) => {
module.exports = {
create(context) {
const reportIfError = (node, sourceCode) => {
const reportIfError = (node, sourceCode) => {
const matches = sourceCode.text.match(nonAsciiRegexPattern);
const matches = sourceCode.text.match(nonAsciiRegexPattern);
if (!matches) return;
if (!matches) return;
const offendingCharacter = matches[0];
const offendingCharacterPosition = matches.index;
const suggestion = suggestions[offendingCharacter];
const offendingCharacter = matches[0];
const offendingCharacterPosition = matches.index;
const suggestion = suggestions[offendingCharacter];
let message = `Non-ASCII character '${offendingCharacter}' detected.`;
let message = `Non-ASCII character '${offendingCharacter}' detected.`;
message = suggestion ?
`${message} Consider replacing with: ${suggestion}` :
message;
message = suggestion ?
`${message} Consider replacing with: ${suggestion}` :
message;
context.report({
node,
message,
loc: sourceCode.getLocFromIndex(offendingCharacterPosition),
});
};
context.report({
node,
message,
loc: sourceCode.getLocFromIndex(offendingCharacterPosition),
});
};
return {
Program: (node) => reportIfError(node, context.getSourceCode()),
};
return {
Program: (node) => reportIfError(node, context.sourceCode),
};
},
};

View file

@ -12,7 +12,7 @@ module.exports = {
fixable: 'code',
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let assertImported = false;
function hasSameTokens(nodeA, nodeB) {

View file

@ -31,7 +31,7 @@ module.exports = {
node,
message: parseError(assertMethod, arg.operator),
fix: (fixer) => {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const left = sourceCode.getText(arg.left);
const right = sourceCode.getText(arg.right);
return fixer.replaceText(

View file

@ -3,64 +3,66 @@
const mustCall = 'CallExpression[callee.object.name="common"]' +
'[callee.property.name="mustCall"]';
module.exports = (context) => {
function isAssertIfError(node) {
return node.type === 'MemberExpression' &&
node.object.type === 'Identifier' &&
node.object.name === 'assert' &&
node.property.type === 'Identifier' &&
node.property.name === 'ifError';
}
function isCallToIfError(node, errName) {
return node.type === 'CallExpression' &&
isAssertIfError(node.callee) &&
node.arguments.length > 0 &&
node.arguments[0].type === 'Identifier' &&
node.arguments[0].name === errName;
}
function bodyStartsWithCallToIfError(body, errName) {
while (body.type === 'BlockStatement' && body.body.length > 0)
body = body.body[0];
let expr;
switch (body.type) {
case 'ReturnStatement':
expr = body.argument;
break;
case 'ExpressionStatement':
expr = body.expression;
break;
default:
expr = body;
module.exports = {
create(context) {
function isAssertIfError(node) {
return node.type === 'MemberExpression' &&
node.object.type === 'Identifier' &&
node.object.name === 'assert' &&
node.property.type === 'Identifier' &&
node.property.name === 'ifError';
}
return isCallToIfError(expr, errName);
}
function isCallToIfError(node, errName) {
return node.type === 'CallExpression' &&
isAssertIfError(node.callee) &&
node.arguments.length > 0 &&
node.arguments[0].type === 'Identifier' &&
node.arguments[0].name === errName;
}
return {
[`${mustCall}:exit`]: (mustCall) => {
if (mustCall.arguments.length > 0) {
const callback = mustCall.arguments[0];
if (isAssertIfError(callback)) {
context.report(mustCall, 'Please use common.mustSucceed instead of ' +
'common.mustCall(assert.ifError).');
}
function bodyStartsWithCallToIfError(body, errName) {
while (body.type === 'BlockStatement' && body.body.length > 0)
body = body.body[0];
if (callback.type === 'ArrowFunctionExpression' ||
callback.type === 'FunctionExpression') {
if (callback.params.length > 0 &&
callback.params[0].type === 'Identifier') {
const errName = callback.params[0].name;
if (bodyStartsWithCallToIfError(callback.body, errName)) {
context.report(mustCall, 'Please use common.mustSucceed instead' +
' of common.mustCall with' +
' assert.ifError.');
let expr;
switch (body.type) {
case 'ReturnStatement':
expr = body.argument;
break;
case 'ExpressionStatement':
expr = body.expression;
break;
default:
expr = body;
}
return isCallToIfError(expr, errName);
}
return {
[`${mustCall}:exit`]: (mustCall) => {
if (mustCall.arguments.length > 0) {
const callback = mustCall.arguments[0];
if (isAssertIfError(callback)) {
context.report(mustCall, 'Please use common.mustSucceed instead of ' +
'common.mustCall(assert.ifError).');
}
if (callback.type === 'ArrowFunctionExpression' ||
callback.type === 'FunctionExpression') {
if (callback.params.length > 0 &&
callback.params[0].type === 'Identifier') {
const errName = callback.params[0].name;
if (bodyStartsWithCallToIfError(callback.body, errName)) {
context.report(mustCall, 'Please use common.mustSucceed instead' +
' of common.mustCall with' +
' assert.ifError.');
}
}
}
}
}
},
};
},
};
},
};

View file

@ -75,9 +75,27 @@ module.exports = {
messages: {
error: 'Use `const { {{name}} } = primordials;` instead of the global.',
},
schema: {
type: 'array',
items: [
{
type: 'object',
required: ['name'],
properties: {
name: { type: 'string' },
ignore: {
type: 'array',
items: { type: 'string' },
},
into: { type: 'string' },
},
additionalProperties: false,
},
],
},
},
create(context) {
const globalScope = context.getSourceCode().scopeManager.globalScope;
const globalScope = context.sourceCode.scopeManager.globalScope;
const nameMap = new Map();
const renameMap = new Map();
@ -110,7 +128,7 @@ module.exports = {
}
const name = node.name;
const parent = getDestructuringAssignmentParent(
context.getScope(),
context.sourceCode.getScope(node),
node,
);
const parentName = parent?.name;
@ -155,7 +173,7 @@ module.exports = {
}
const variables =
context.getSourceCode().scopeManager.getDeclaredVariables(node);
context.sourceCode.scopeManager.getDeclaredVariables(node);
if (variables.length === 0) {
context.report({
node,

View file

@ -7,8 +7,8 @@
'use strict';
// Cribbed from `eslint-module-utils/declaredScope`
function declaredScope(context, name) {
const references = context.getScope().references;
function declaredScope(context, node, name) {
const references = context.sourceCode.getScope(node).references;
const reference = references.find((x) => x.identifier.name === name);
if (!reference) return undefined;
return reference.resolved.scope.type;
@ -33,12 +33,12 @@ module.exports = {
[callee.type="MemberExpression"][callee.object.name="Object"][callee.property.name="create"]\
)'(node) {
if (node.callee.type === 'MemberExpression') {
const scope = declaredScope(context, node.callee.object);
const scope = declaredScope(context, node, node.callee.object);
if (scope && scope !== 'module' && scope !== 'global') {
return;
}
}
const value = context.getSourceCode().getText(node.arguments[0]);
const value = context.sourceCode.getText(node.arguments[0]);
context.report({
node,
messageId: 'error',

View file

@ -46,7 +46,7 @@ module.exports = {
message: 'Every object must have __proto__: null',
fix: function(fixer) {
// Generate the fix suggestion to add __proto__: null
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const firstProperty = properties[0];
const firstPropertyToken = sourceCode.getFirstToken(firstProperty);