@putout/compare
Version:
compare AST-nodes according to 🦎 PutoutScript
361 lines (270 loc) • 7.97 kB
JavaScript
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();