UNPKG

@exadel/eslint-plugin-esl

Version:

Helper ESLint rules to find and migrate ESL (@exadel/esl) library deprecations

114 lines (113 loc) 4.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildRule = buildRule; const ast_utils_1 = require("./ast.utils"); const meta = { type: 'suggestion', docs: { description: 'replace deprecated aliases', recommended: true, }, fixable: 'code' }; /** Builds deprecation rule from {@link ESLintDeprecationCfg} object */ function buildRule(configs) { configs = Array.isArray(configs) ? configs : [configs]; const create = (context) => ({ ImportSpecifier(node) { const importedValue = node.imported; if (importedValue.type !== 'Identifier') return null; configs.forEach((config) => { if (importedValue.name === config.deprecation) { context.report({ node, message: `[ESL Lint]: Deprecated alias ${config.deprecation} for ${config.alias}`, fix: buildFixer(node, context, config.alias) }); } }); return null; } }); return { meta, create }; } /** * Creates fixe-list for the node of incorrect import * @param context - AST tree object * @param node - import node to process * @param alias - current name */ function buildFixer(node, context, alias) { return (fixer) => { const ranges = getIdentifierRanges(node, context); return ranges.map((range) => fixer.replaceTextRange(range, alias)); }; } /** * Find usage's ranges of the deprecated alias * @param context - AST tree object * @param importNode - import node to process */ // eslint-disable-next-line sonarjs/cognitive-complexity function getIdentifierRanges(importNode, context) { var _a; const root = (0, ast_utils_1.findRoot)(importNode); if (importNode.imported.type !== 'Identifier' || !root) return []; const { name } = importNode.imported; const identifiers = (0, ast_utils_1.findAllBy)(context, root, { type: 'Identifier', name }); const overrides = []; const occurrences = new Set(); for (const idNode of identifiers) { const { parent } = idNode; if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'MemberExpression') { if (parent.object.name === name) occurrences.add(parent.object); } else if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'VariableDeclarator') { overrides.push(parent); } else { occurrences.add(idNode); } } for (const declaration of overrides) { const scope = getScopeNode(declaration); if (!scope) continue; const nestedNodes = (0, ast_utils_1.findAllBy)(context, scope, { type: 'Identifier', name }); for (const node of nestedNodes) { if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) !== 'ImportSpecifier') { occurrences.delete(node); } } const initExpNodes = (0, ast_utils_1.findAllBy)(context, declaration.init, { type: 'Identifier', name }); for (const node of initExpNodes) { occurrences.add(node); } } return getRanges(occurrences); } function getScopeNode(declaration) { var _a; let node = declaration.parent; if (!node) return null; const isBlockScoped = node.kind && (node.kind === 'const' || node.kind === 'let'); while (node.parent) { node = node.parent; if (node.type === 'BlockStatement' && (isBlockScoped || ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === 'FunctionExpression')) return node; } return node; } function getRanges(nodes) { const uniqNodes = []; for (const node of nodes) { if (!uniqNodes.some((item) => String(item.range) === String(node.range))) { uniqNodes.push(node); } } return uniqNodes.map((node) => node.range); }