eslint-plugin-sonarjs
Version:
SonarJS rules for ESLint
106 lines (105 loc) • 4.6 kB
JavaScript
;
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// https://sonarsource.github.io/rspec/#/rspec/S1186/javascript
Object.defineProperty(exports, "__esModule", { value: true });
exports.reportWithQuickFixIfApplicable = exports.decorate = void 0;
const helpers_1 = require("../helpers");
const meta_1 = require("./meta");
function isRuleFunctionNode(node) {
return (0, helpers_1.isFunctionNode)(node) && 'parent' in node;
}
// core implementation of this rule does not provide quick fixes
function decorate(rule) {
return (0, helpers_1.interceptReport)({
...rule,
meta: (0, helpers_1.generateMeta)(meta_1.meta, {
...rule.meta,
hasSuggestions: true,
}),
}, reportWithQuickFixIfApplicable);
}
exports.decorate = decorate;
function reportWithQuickFixIfApplicable(context, reportDescriptor) {
if (!('node' in reportDescriptor) || !isRuleFunctionNode(reportDescriptor.node)) {
return;
}
const functionNode = reportDescriptor.node;
if (isApplicable(functionNode)) {
reportWithQuickFix(context, reportDescriptor, functionNode);
}
}
exports.reportWithQuickFixIfApplicable = reportWithQuickFixIfApplicable;
// This function limits the issues to variable/function/method declarations which name is not like /^on[A-Z].
// Any lambda expression or arrow function is thus ignored.
function isApplicable(functionNode) {
// Matches identifiers like onClick and more generally onXxx as well as XXXnoopXXX functions
function isExceptionalName(node) {
return (node !== null && (0, helpers_1.isIdentifier)(node) && (/^on[A-Z]/.test(node.name) || /noop/i.test(node.name)));
}
// Matches: function foo() {}
// But not: function onClose() {} or function XXXnoopXXX() {}
function isFunctionDeclaration() {
return functionNode.type === 'FunctionDeclaration' && !isExceptionalName(functionNode.id);
}
// Matches: class A { foo() {} }
// But not: class A { onClose() {} }
function isMethodDefinition() {
const methodNode = functionNode.parent;
return (methodNode.type === 'MethodDefinition' &&
methodNode.value === functionNode &&
!isExceptionalName(methodNode.key));
}
// Matches: const foo = () => {};
// But not: const onClose = () => {};
function isVariableDeclarator() {
const variableNode = functionNode.parent;
return (variableNode.type === 'VariableDeclarator' &&
variableNode.init === functionNode &&
!isExceptionalName(variableNode.id));
}
return isFunctionDeclaration() || isMethodDefinition() || isVariableDeclarator();
}
function reportWithQuickFix(context, reportDescriptor, func) {
const name = reportDescriptor.data.name;
const openingBrace = context.sourceCode.getFirstToken(func.body);
const closingBrace = context.sourceCode.getLastToken(func.body);
suggestEmptyBlockQuickFix(context, reportDescriptor, name, openingBrace, closingBrace);
}
function suggestEmptyBlockQuickFix(context, descriptor, blockType, openingBrace, closingBrace) {
let commentPlaceholder;
if (openingBrace.loc.start.line === closingBrace.loc.start.line) {
commentPlaceholder = ` /* TODO document why this ${blockType} is empty */ `;
}
else {
const columnOffset = closingBrace.loc.start.column;
const padding = ' '.repeat(columnOffset);
commentPlaceholder = `\n${padding} // TODO document why this ${blockType} is empty\n${padding}`;
}
context.report({
...descriptor,
suggest: [
{
desc: 'Insert placeholder comment',
fix: fixer => fixer.insertTextAfter(openingBrace, commentPlaceholder),
},
],
});
}