Skip to content

Commit

Permalink
tools: prepare custom rules for ESLint v9
Browse files Browse the repository at this point in the history
Refs: https://eslint.org/docs/latest/use/migrate-to-9.0.0
PR-URL: #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>
  • Loading branch information
targos authored and marco-ippolito committed Jun 17, 2024
1 parent 46020d8 commit 3d13a6a
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 114 deletions.
2 changes: 1 addition & 1 deletion tools/eslint-rules/no-array-destructuring.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module.exports = {
schema: [],
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

return {
ArrayPattern(node) {
Expand Down
64 changes: 33 additions & 31 deletions tools/eslint-rules/no-duplicate-requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
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();
}

const required = new Set();
return undefined;
}

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);
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;
return rules;
},
};
2 changes: 1 addition & 1 deletion tools/eslint-rules/no-unescaped-regexp-dot.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

module.exports = {
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const regexpStack = [];
let regexpBuffer = [];
let inRegExp = false;
Expand Down
42 changes: 22 additions & 20 deletions tools/eslint-rules/non-ascii-character.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
},
};
2 changes: 1 addition & 1 deletion tools/eslint-rules/prefer-assert-iferror.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion tools/eslint-rules/prefer-assert-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
104 changes: 53 additions & 51 deletions tools/eslint-rules/prefer-common-mustsucceed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
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';
}

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 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];
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;
}
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 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).');
}
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.');
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.');
}
}
}
}
}
},
};
},
};
},
};
24 changes: 21 additions & 3 deletions tools/eslint-rules/prefer-primordials.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -110,7 +128,7 @@ module.exports = {
}
const name = node.name;
const parent = getDestructuringAssignmentParent(
context.getScope(),
context.sourceCode.getScope(node),
node,
);
const parentName = parent?.name;
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions tools/eslint-rules/prefer-proto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion tools/eslint-rules/set-proto-to-null-in-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 3d13a6a

Please sign in to comment.