UNPKG

@ui5/linter

Version:

A static code analysis tool for UI5

105 lines 4.15 kB
import ts from "typescript"; import { isAssignment } from "../utils/utils.js"; export default function getGlobalFixInfo(node, ambientModuleCache) { if (ts.isCallExpression(node)) { if (!ts.isIdentifier(node.expression) || !["$", "jQuery"].includes(node.expression.text)) { return; } // TODO return; } if (isAssignment(node)) { // Can't fix assignments return; } return getImportFromGlobal(node, ambientModuleCache); } function getImportFromGlobal(node, ambientModuleCache) { const parts = []; const partNodes = []; const firstPart = node.expression; if (!ts.isIdentifier(firstPart)) { return undefined; } if (firstPart.text !== "window" && firstPart.text !== "globalThis" && firstPart.text !== "self") { parts.push(firstPart.text); partNodes.push(firstPart); } let scanNode = node; while (ts.isPropertyAccessExpression(scanNode)) { if (!ts.isIdentifier(scanNode.name)) { throw new Error(`Unexpected PropertyAccessExpression node: Expected name to be identifier but got ` + ts.SyntaxKind[scanNode.name.kind]); } parts.push(scanNode.name.text); partNodes.push(scanNode); scanNode = scanNode.parent; } let moduleSymbol; const searchStack = [...parts]; let exportName; while (!moduleSymbol && searchStack.length) { const moduleName = searchStack.join("/"); if (moduleName === "sap/ui/core/Core") { // Special case for sap.ui.core.Core: // The global access provides the Core class, while the module import // only provides the singleton instance. // For now, we don't fix this case, because it is not a common pattern. return undefined; } // TODO: Handle global usage of jQuery/$, to be replaced with an import of sap/ui/thirdparty/jquery // Note that there might be more handling needed than just this code here. // if (moduleName === "jQuery" || moduleName === "$") { // return { // fixHints: {moduleName: "sap/ui/thirdparty/jquery", propertyAccess: searchStack.join(".")}, // propertyAccessNode: partNodes[searchStack.length - 1], // }; // } moduleSymbol = ambientModuleCache.findModuleForName(moduleName); if (!moduleSymbol) { const libraryModuleName = `${moduleName}/library`; moduleSymbol = ambientModuleCache.findModuleForName(libraryModuleName); if (moduleSymbol) { exportName = parts[searchStack.length]; if (exportName && !isLibraryExportAccess(moduleSymbol, parts.slice(searchStack.length))) { return undefined; } return { moduleName: libraryModuleName, propertyAccessStack: searchStack.reverse(), relevantNode: partNodes[searchStack.length - 1], }; } } if (!moduleSymbol) { searchStack.pop(); } } if (!searchStack.length) { return undefined; } return { moduleName: searchStack.join("/"), propertyAccessStack: searchStack.reverse(), relevantNode: partNodes[searchStack.length - 1], }; } function isLibraryExportAccess(moduleSymbol, parts) { // Check for access of unknown/private export or a global usage without a corresponding module // e.g. when defining a shortcut for a sub-namespace like sap.ui.core.message let currentSymbol = moduleSymbol; let currentPart; while (parts.length) { currentPart = parts.shift(); currentSymbol = currentSymbol.exports?.get(currentPart); if (!currentSymbol) { return false; } // Only continue when symbol is a namespace, as only those have exports we want to check for if (!(currentSymbol.flags & ts.SymbolFlags.Namespace)) { return true; } } return true; } //# sourceMappingURL=getGlobalFixInfo.js.map