UNPKG

eslint-codemod-utils

Version:

A collection of AST helper functions for more complex ESLint rule fixes.

139 lines (138 loc) 5.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.removeImportSpecifier = exports.insertImportDeclaration = exports.insertImportSpecifier = exports.hasImportSpecifier = exports.hasImportDeclaration = exports.hasJSXChild = exports.hasJSXAttribute = void 0; const nodes_1 = require("../nodes"); const is_node_of_type_1 = require("./is-node-of-type"); const types_1 = require("@typescript-eslint/types"); function hasJSXAttribute(node, attributeName) { if (!node.openingElement) return false; if (!node.openingElement.attributes.length) return false; return node.openingElement.attributes.some((attr) => (0, is_node_of_type_1.isNodeOfType)(attr, types_1.AST_NODE_TYPES.JSXAttribute) && attr.name.name === attributeName); } exports.hasJSXAttribute = hasJSXAttribute; function hasJSXChild(node, childIdentifier) { const jsxIdentifierMatch = (0, is_node_of_type_1.isNodeOfType)(node.openingElement.name, types_1.AST_NODE_TYPES.JSXIdentifier) && node.openingElement.name.name && node.openingElement.name.name === childIdentifier; return (jsxIdentifierMatch || Boolean(node.children && node.children .filter((child) => (0, is_node_of_type_1.isNodeOfType)(child, types_1.AST_NODE_TYPES.JSXElement)) .find((child) => hasJSXChild(child, childIdentifier)))); } exports.hasJSXChild = hasJSXChild; /** * Whether a declaration does or does not include a specified source. * * @param declaration * @param source * @returns */ function hasImportDeclaration(declaration, source) { return declaration.source.value === source; } exports.hasImportDeclaration = hasImportDeclaration; /** * * @param declaration * @param specifierId */ function hasImportSpecifier(declaration, importName) { if (importName === 'default') { return declaration.specifiers.some((spec) => (0, is_node_of_type_1.isNodeOfType)(spec, types_1.AST_NODE_TYPES.ImportDefaultSpecifier)); } return declaration.specifiers .filter((spec) => (0, is_node_of_type_1.isNodeOfType)(spec, types_1.AST_NODE_TYPES.ImportSpecifier)) .some((node) => node.imported.name === importName); } exports.hasImportSpecifier = hasImportSpecifier; /** * Appends or adds an import specifier to an existing import declaration. * * Does not validate whether the insertion is already present. * * @param declaration * @param importName * @param specifierAlias * @returns {StringableASTNode<ImportDeclaration>} */ function insertImportSpecifier(declaration, importName, specifierAlias) { if (importName === 'default' && !specifierAlias) { throw new Error('A specifier name must be provided when inserting the default import.'); } const id = (0, nodes_1.identifier)(importName); const newSpecifier = importName === 'default' ? // Narrowed above — `specifierAlias` is guaranteed non-undefined here. (0, nodes_1.importDefaultSpecifier)({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion local: (0, nodes_1.identifier)(specifierAlias), }) : (0, nodes_1.importSpecifier)({ imported: (0, nodes_1.identifier)(importName), local: specifierAlias ? (0, nodes_1.identifier)(specifierAlias) : id, }); // `.concat` on a heterogenous specifier array widens to the union — build // the array explicitly to keep types consistent with `WithoutType<…>`. return (0, nodes_1.importDeclaration)({ ...declaration, specifiers: [...declaration.specifiers, newSpecifier], }); } exports.insertImportSpecifier = insertImportSpecifier; /** * @example * ```tsx * insertImportDeclaration('source', ['specifier', 'second']) * * // produces * import { specifier, second } from 'source' * ``` * * @example * ```tsx * * insertImportDeclaration('source', ['specifier', { imported: 'second', local: 'other' }]) * * // produces * import { specifier, second as other } from 'source' * ``` */ function insertImportDeclaration(source, specifiers) { return (0, nodes_1.importDeclaration)({ source: (0, nodes_1.literal)(source), specifiers: specifiers.map((spec) => { return spec === 'default' ? (0, nodes_1.importDefaultSpecifier)({ local: (0, nodes_1.identifier)('__default'), }) : (0, nodes_1.importSpecifier)({ imported: typeof spec === 'string' ? (0, nodes_1.identifier)(spec) : (0, nodes_1.identifier)(spec.imported), local: typeof spec === 'string' ? (0, nodes_1.identifier)(spec) : (0, nodes_1.identifier)(spec.local), }); }), }); } exports.insertImportDeclaration = insertImportDeclaration; /** * Removes an import specifier to an existing import declaration. * * @param declaration * @param importName * @returns {StringableASTNode<ESTree.ImportDeclaration>} */ function removeImportSpecifier(declaration, importName) { return (0, nodes_1.importDeclaration)({ ...declaration, specifiers: declaration.specifiers.filter((spec) => importName === 'default' ? spec.type !== 'ImportDefaultSpecifier' : !((0, is_node_of_type_1.isNodeOfType)(spec, types_1.AST_NODE_TYPES.ImportSpecifier) && spec.imported.name === importName)), }); } exports.removeImportSpecifier = removeImportSpecifier;