UNPKG

eslint-plugin-sonarjs

Version:
221 lines (220 loc) 8.93 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getFullyQualifiedNameTS = getFullyQualifiedNameTS; exports.importsModuleTS = importsModuleTS; /* * SonarQube JavaScript Plugin * Copyright (C) SonarSource Sàrl * mailto:info AT sonarsource DOT com * * You can redistribute and/or modify this program under the terms of * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. * * 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/ */ const typescript_1 = __importDefault(require("typescript")); const ancestor_js_1 = require("./ancestor.js"); const module_js_1 = require("./module.js"); const FILE_IMPORTS_CACHE = new WeakMap(); function getFullyQualifiedNameTS(services, rootNode) { const result = []; let node = rootNode; const visitedNodes = new Set(); while (node) { if (visitedNodes.has(node)) { return null; } visitedNodes.add(node); switch (node.kind) { case typescript_1.default.SyntaxKind.CallExpression: { const callExpressionNode = node; if (isRequireCall(callExpressionNode)) { node = callExpressionNode.arguments.at(0); } else { node = callExpressionNode.expression; } break; } case typescript_1.default.SyntaxKind.FunctionDeclaration: { const functionDeclarationNode = node; const name = functionDeclarationNode.name?.text; if (!name) { return null; } result.push(name); node = functionDeclarationNode.parent; break; } case typescript_1.default.SyntaxKind.PropertyAccessExpression: { const propertyAccessExpression = node; const rhsFQN = propertyAccessExpression.name.text; if (!rhsFQN) { return null; } result.push(rhsFQN); node = propertyAccessExpression.expression; break; } case typescript_1.default.SyntaxKind.ImportSpecifier: { const importSpecifier = node; const identifierName = importSpecifier.propertyName?.text ?? importSpecifier.name.text; if (!identifierName) { return null; } result.push(identifierName); node = importSpecifier.parent; break; } case typescript_1.default.SyntaxKind.NamespaceImport: { node = node.parent; break; } case typescript_1.default.SyntaxKind.ImportDeclaration: { node = node.moduleSpecifier; break; } case typescript_1.default.SyntaxKind.SourceFile: { // Don't generate fqn for local files return null; } case typescript_1.default.SyntaxKind.BindingElement: { const bindingElement = node; let identifier; if (bindingElement.propertyName && 'text' in bindingElement.propertyName) { identifier = bindingElement.propertyName.text; } else if ('text' in bindingElement.name) { identifier = bindingElement.name.text; } if (!identifier) { return null; } result.push(identifier); node = node.parent; break; } case typescript_1.default.SyntaxKind.VariableDeclaration: { const variableDeclaration = node; if (variableDeclaration.initializer) { node = variableDeclaration.initializer; break; } else { return null; } } case typescript_1.default.SyntaxKind.Identifier: { const identifierSymbol = services.program.getTypeChecker().getSymbolAtLocation(node); const declaration = identifierSymbol?.declarations?.at(0); // Handle: no symbol info, compiler module, self-referential declaration (e.g., `module` in CommonJS), // or declaration that contains the root node (e.g., `const geo = geo(request)` where import is shadowed) if (isCompilerModule(identifierSymbol) || !declaration || declaration === node || (0, ancestor_js_1.isTsAncestor)(declaration, rootNode)) { result.push(node.text); return returnResult(); } else { node = declaration; break; } } case typescript_1.default.SyntaxKind.StringLiteral: { result.push(node.text); return returnResult(); } case typescript_1.default.SyntaxKind.ImportClause: // Fallthrough case typescript_1.default.SyntaxKind.ObjectBindingPattern: // Fallthrough case typescript_1.default.SyntaxKind.Block: // Fallthrough case typescript_1.default.SyntaxKind.ExpressionStatement: // Fallthrough case typescript_1.default.SyntaxKind.NamedImports: // Fallthrough case typescript_1.default.SyntaxKind.ModuleBlock: { node = node.parent; break; } default: { return null; } } } return null; function returnResult() { result.reverse(); return (0, module_js_1.removeNodePrefixIfExists)(result.join('.')); } } function importsModuleTS(sourceFile, moduleNames) { if (moduleNames.length === 0) { return false; } const imports = getImportsOfSourceFile(sourceFile); return moduleNames.some(moduleName => imports.has(moduleName)); } function getImportsOfSourceFile(sourceFile) { let imports = FILE_IMPORTS_CACHE.get(sourceFile); if (imports !== undefined) { return imports; } imports = new Set(); for (const statement of sourceFile.statements) { const importedModuleName = getImportedModuleName(statement); if (importedModuleName !== undefined) { imports.add(importedModuleName); continue; } if (!typescript_1.default.isVariableStatement(statement)) { continue; } for (const moduleName of getRequiredModuleNames(statement)) { imports.add(moduleName); } } FILE_IMPORTS_CACHE.set(sourceFile, imports); return imports; } function getImportedModuleName(statement) { if (!typescript_1.default.isImportDeclaration(statement) || !typescript_1.default.isStringLiteral(statement.moduleSpecifier)) { return undefined; } return statement.moduleSpecifier.text; } function getRequiredModuleNames(variableStatement) { return variableStatement.declarationList.declarations.flatMap(declaration => { const moduleName = getRequiredModuleName(declaration.initializer); return moduleName === undefined ? [] : [moduleName]; }); } function isRequireCall(callExpression) { return (callExpression.expression.kind === typescript_1.default.SyntaxKind.Identifier && callExpression.expression.text === 'require' && callExpression.arguments.length === 1); } function getRequiredModuleName(initializer) { if (initializer === undefined) { return undefined; } if (typescript_1.default.isPropertyAccessExpression(initializer)) { return getRequiredModuleName(initializer.expression); } if (!typescript_1.default.isCallExpression(initializer) || !isRequireCall(initializer)) { return undefined; } const moduleName = initializer.arguments[0]; return typescript_1.default.isStringLiteral(moduleName) ? moduleName.text : undefined; } function isCompilerModule(node) { return (node && (node.flags & typescript_1.default.SymbolFlags.Module) !== 0 && (node.flags & typescript_1.default.SymbolFlags.Assignment) !== 0); }