UNPKG

@putout/compare

Version:

compare AST-nodes according to 🦎 PutoutScript

361 lines (270 loc) • 7.97 kB
import {template} from '@putout/engine-parser'; import {types} from '@putout/babel'; const { isBlockStatement, isTSModuleBlock, isBooleanLiteral, isIdentifier, isLiteral, isTemplateElement, isFunction, isImportDefaultSpecifier, isExportSpecifier, isRegExpLiteral, isJSXText, isJSXIdentifier, isJSXAttribute, isTSTypeReference, isTSTypeParameterDeclaration, } = types; export const isStr = (a) => typeof a === 'string'; const ANY_OBJECT = '__object'; const ANY_ARRAY = '__array'; const ARGS = '__args'; const TYPE_PARAMS = '__type_params'; const IMPORTS = '__imports'; const EXPORTS = '__exports'; const BODY = '__body'; const JSX_CHILDREN = '__jsx_children'; const JSX_ATTRIBUTES = '__jsx_attributes'; const NOP = '__nop'; const ANY = '__'; const ID = '__identifier'; const BOOL = '__bool'; const LINKED_NODE = /^__[a-z]$/; const LINKED_ARGS = /__args__[a-z]$/; const LINKED_ID = /^__identifier__[a-z]$/; const LINKED_BOOL = /^__bool__[a-z]$/; const ALL = [ ANY_OBJECT, ANY_ARRAY, ARGS, TYPE_PARAMS, BOOL, JSX_CHILDREN, JSX_ATTRIBUTES, IMPORTS, EXPORTS, BODY, ANY, ID, LINKED_NODE, LINKED_ARGS, LINKED_ID, LINKED_BOOL, ]; export const isTemplate = (a) => /[(;={.\s]/.test(a) || !/^[A-Z]/.test(a); export const is = (str, array = ALL) => { for (const item of array) { if (check(str, item)) return true; } return false; }; function check(str, item) { if (isStr(item)) return str === item; return item.test(str); } export const isNameStr = (a) => LINKED_NODE.test(a); export const isImportsStr = (a) => a === IMPORTS; export const isExportsStr = (a) => a === EXPORTS; export const isArgsStr = (a) => a === ARGS || LINKED_ARGS.test(a); export const isTypeParamsStr = (a) => a === TYPE_PARAMS; export const isJSXChildrenStr = (a) => a === JSX_CHILDREN; export const isJSXAttributesStr = (a) => a === JSX_ATTRIBUTES; export const isObjectStr = (a) => a === ANY_OBJECT; export const isArrayStr = (a) => a === ANY_ARRAY; export const isAnyStr = (a) => a === ANY; export const isBodyStr = (a) => a === BODY; const isBody = (a) => isIdentifier(a, { name: BODY, }); const isFunctionDeclarationBody = (a) => { if (isBody(a)) return true; if (!isBlockStatement(a) && !isTSModuleBlock(a)) return false; return isBody(a.body[0].expression); }; const isNop = (a) => isIdentifier(a, { name: NOP, }); const isAnyObject = (a) => isIdentifier(a, { name: ANY_OBJECT, }); const isAnyArray = (a) => isIdentifier(a, { name: ANY_ARRAY, }); export const isId = (a, b) => { if (!isIdentifier(b, {name: ID})) return false; return isIdentifier(a); }; export const isBool = (a, b) => { if (!isIdentifier(b, {name: BOOL})) return false; return isBooleanLiteral(a); }; export const isEqualType = (a, b) => a.type === b.type; export const {isArray} = Array; export const isAny = (a) => { if (isIdentifier(a, {name: ANY})) return true; return isJSXText(a, { value: ANY, }); }; export const isAnyLiteral = (a, b) => { if (!isLiteral(b, {value: ANY})) return false; return isEqualType(a, b); }; export const isArgs = (a) => { const b = !isArray(a) ? a : a[0]; return isIdentifier(b, { name: ARGS, }); }; const isTypeParams = (node) => { if (!isTSTypeParameterDeclaration(node)) return false; const {params} = node; const {name} = params[0]; return isIdentifier(name, { name: TYPE_PARAMS, }); }; export const isEqualTypeParams = (a, b) => { if (!a) return false; if (!isTypeParams(b)) return false; return isEqualType(a, b); }; export const isLinkedArgs = (a) => { const b = !isArray(a) ? a : a[0]; return isIdentifier(b) && LINKED_ARGS.test(b.name); }; export const isJSXChildren = (a) => { const b = !isArray(a) ? a : a[0]; return isJSXText(b, { value: JSX_CHILDREN, }); }; export const isJSXAttributes = (a) => { const b = !isArray(a) ? a : a[0]; if (!isJSXAttribute(b)) return false; return isJSXIdentifier(b.name, { name: JSX_ATTRIBUTES, }); }; export const isLinkedId = (a, b) => { if (!isIdentifier(b) || !LINKED_ID.test(b.name)) return false; return isIdentifier(a); }; export const isLinkedBool = (a, b) => { if (!isIdentifier(b) || !LINKED_BOOL.test(b.name)) return false; return isBooleanLiteral(a); }; export const isLinkedRegExp = (a, b) => { if (!isRegExpLiteral(b) || !LINKED_NODE.test(b.pattern)) return false; return isRegExpLiteral(a); }; export const isPath = (path) => Boolean(path.node); export const isObject = (a) => { if (!a) return false; if (isArray(a)) return false; return typeof a === 'object'; }; export const isArrays = (a, b) => { if (!isArray(a) || !isArray(b)) return false; return a.length === b.length; }; export const isImports = (a) => { const b = !isArray(a) ? a : a[0]; if (!isImportDefaultSpecifier(b)) return false; return isIdentifier(b.local, { name: IMPORTS, }); }; export const isExports = (a) => { const b = !isArray(a) ? a : a[0]; if (!isExportSpecifier(b)) return false; return isIdentifier(b.local, { name: EXPORTS, }); }; const __OBJECT_TYPE = 'ObjectPattern|ObjectExpression'; const __ARRAY_TYPE = 'ArrayPattern|ArrayExpression'; export const isEqualAnyArray = (node, templateNode) => { if (!isAnyArray(templateNode)) return false; const {type} = node; return __ARRAY_TYPE.includes(type); }; export const isEqualAnyObject = (node, templateNode) => { if (!isAnyObject(templateNode)) return false; const {type} = node; return __OBJECT_TYPE.includes(type); }; export const isEqualBody = (node, templateNode) => { if (!node) return false; if (!isBody(templateNode)) return false; return node.type === 'BlockStatement'; }; export const isEqualFunctionDeclarationBody = (node, templateNode) => { if (!node) return false; if (!isFunctionDeclarationBody(templateNode)) return false; const {type} = node; return /BlockStatement|TSModuleBlock/.test(type); }; export const isEqualNop = (node, templateNode) => { if (!isNop(templateNode)) return false; if (!isFunction(node)) return false; const {body} = node; if (!isBlockStatement(body)) return false; return !body.body.length; }; export const isLinkedNode = (a) => { if (isIdentifier(a) && LINKED_NODE.test(a.name)) return true; if (isLiteral(a) && LINKED_NODE.test(a.value)) return true; if (isJSXText(a) && LINKED_NODE.test(a.value)) return true; if (isJSXIdentifier(a) && LINKED_NODE.test(a.name)) return true; if (isTemplateElement(a) && LINKED_NODE.test(a.value.raw)) return true; return isTSTypeReference(a) && LINKED_NODE.test(a.typeName.name); }; export const parseTemplate = (tmpl, {program} = {}) => { const parse = !program ? template.ast : template.program.ast; const node = parse(tmpl) || template.ast.fresh(tmpl); if (tmpl === ANY_OBJECT) return [node, __OBJECT_TYPE]; if (tmpl === ANY_ARRAY) return [node, __ARRAY_TYPE]; const {type} = node; return [node, type]; }; export const isInsideTypeReference = (path) => path.isIdentifier() && path.parentPath?.isTSTypeReference(); export const isInsideTypeParameter = (path) => path.isIdentifier() && path.parentPath?.isTSTypeParameter();