UNPKG

eslint-plugin-sonarjs

Version:
108 lines (107 loc) 4.36 kB
"use strict"; /* * SonarQube JavaScript Plugin * Copyright (C) 2011-2025 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 Sonar Source-Available License Version 1, as published by SonarSource SA. * * 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 Sonar Source-Available License for more details. * * You should have received a copy of the Sonar Source-Available License * along with this program; if not, see https://sonarsource.com/license/ssal/ */ // https://sonarsource.github.io/rspec/#/rspec/S1481/javascript Object.defineProperty(exports, "__esModule", { value: true }); exports.rule = void 0; const index_js_1 = require("../helpers/index.js"); const meta_js_1 = require("./meta.js"); exports.rule = { meta: (0, index_js_1.generateMeta)(meta_js_1.meta, { messages: { unusedFunction: `Remove unused function '{{symbol}}'.`, unusedVariable: `Remove the declaration of the unused '{{symbol}}' variable.`, }, }), create(context) { let toIgnore = []; let jsxComponentsToIgnore = []; function checkVariable(v, toCheck) { if (v.defs.length === 0) { return; } const type = v.defs[0].type; if (type !== 'Variable' && type !== 'FunctionName') { return; } if (toCheck === 'let-const-function') { const def = v.defs[0]; if (def.parent && def.parent.type === 'VariableDeclaration' && def.parent.kind === 'var') { return; } } const defs = v.defs.map(def => def.name); const unused = v.references.every(ref => defs.includes(ref.identifier)); if (unused && !toIgnore.includes(defs[0]) && !jsxComponentsToIgnore.includes(v.name)) { const messageAndData = getMessageAndData(v.name, type === 'FunctionName'); defs.forEach(def => context.report({ node: def, ...messageAndData, })); } } function isParentOfModuleScope(scope) { return scope.childScopes.some(s => s.type === 'module'); } function checkScope(scope, checkedInParent) { let toCheck = checkedInParent; if (scope.type === 'function' && !isParentOfModuleScope(scope)) { toCheck = 'all'; } else if (checkedInParent === 'nothing' && scope.type === 'block') { toCheck = 'let-const-function'; } if (toCheck !== 'nothing' && scope.type !== 'function-expression-name') { scope.variables.forEach(v => checkVariable(v, toCheck)); } scope.childScopes.forEach(childScope => checkScope(childScope, toCheck)); } return { ObjectPattern: (node) => { const elements = node.properties; const hasRest = elements.some(element => element.type === 'RestElement'); if (!hasRest) { return; } elements.forEach(element => { if (element.type === 'Property' && element.shorthand && element.value.type === 'Identifier') { toIgnore.push(element.value); } }); }, JSXIdentifier: (node) => { // using 'any' as standard typings for AST don't provide types for JSX jsxComponentsToIgnore.push(node.name); }, 'Program:exit': (node) => { checkScope(context.sourceCode.getScope(node), 'nothing'); toIgnore = []; jsxComponentsToIgnore = []; }, }; }, }; function getMessageAndData(name, isFunction) { if (isFunction) { return { messageId: 'unusedFunction', data: { symbol: name } }; } else { return { messageId: 'unusedVariable', data: { symbol: name } }; } }