UNPKG

canonical

Version:

Canonical code style linter and formatter for JavaScript, SCSS, CSS and JSON.

260 lines (237 loc) 7.7 kB
'use strict'; var _ = require('lodash'); /** * Gets the object that called the method in a CallExpression * @param {Object} node * @returns {Object|undefined} */ function getCaller(node) { return _.get(node, 'callee.object'); } /** * Gets the name of a method in a CallExpression * @param {Object} node * @returns {string|undefined} */ function getMethodName(node) { return _.get(node, 'callee.property.name'); } /** * Returns whether the node is a method call * @param {Object} node * @returns {boolean|undefined} */ function isMethodCall(node) { return node && node.type === 'CallExpression' && node.callee.type === 'MemberExpression'; } /** * Returns whether the node is a function declaration that has a block * @param {Object} node * @returns {boolean} */ function isFunctionDefinitionWithBlock(node) { return (node.type === 'FunctionExpression' || (node.type === 'ArrowFunctionExpression' && node.body.type === 'BlockStatement')); } /** * If the node specified is a function, returns the node corresponding with the first statement/expression in that function * @param {Object} node * @returns {node|null} */ function getFirstFunctionLine(node) { if (node) { if (isFunctionDefinitionWithBlock(node)) { return _.get(node, 'body.body[0]'); } if (node.type === 'ArrowFunctionExpression') { return node.body; } } return null; } /** * * @param {Object} node * @returns {boolean|undefined} */ function isPropAccess(node) { return node && node.computed === false || (node.computed === true && node.property.type === 'Literal'); } /** * Returns whether the node is a member expression starting with the same object, up to the specified length * @param {Object} node * @param {string} objectName * @param {number} maxPropertyPathLength * @param {boolean} allowComputed * @returns {boolean|undefined} */ function isMemberExpOf(node, objectName, maxPropertyPathLength, allowComputed) { if (objectName) { var curr = node; var depth = maxPropertyPathLength; while (curr && depth) { if (curr.type === 'MemberExpression' && curr.object.name === objectName) { return allowComputed || isPropAccess(node); } curr = curr.object; depth--; } } } /** * Returns the name of the first parameter of a function, if it exists * @param {Object} func * @returns {string|undefined} */ function getFirstParamName(func) { return _.get(func, 'params[0].name'); } /** * Returns whether or not the expression is a return statement * @param {Object} exp * @returns {boolean|undefined} */ function isReturnStatement(exp) { return exp && exp.type === 'ReturnStatement'; } /** * Returns whether the node specified has only one statement * @param {Object} func * @returns {boolean} */ function hasOnlyOneStatement(func) { return func.type === 'ArrowFunctionExpression' ? !_.get(func, 'body.body') : _.get(func, 'body.body.length') === 1; } /** * Returns whether the node is an object of a method call * @param {Object} node * @returns {boolean} */ function isObjectOfMethodCall(node) { return _.get(node, 'parent.object') === node && _.get(node, 'parent.parent.type') === 'CallExpression'; } /** * Returns whether the expression specified is a binary expression with the specified operator and one of its sides is a member expression of the specified object name * @param {string} operator * @param {Object} exp * @param {string} objectName * @param {number} maxPropertyPathLength * @param {boolean} allowComputed * @returns {boolean|undefined} */ function isBinaryExpWithMemberOf(operator, exp, objectName, maxPropertyPathLength, allowComputed) { return exp && exp.type === 'BinaryExpression' && exp.operator === operator && (isMemberExpOf(exp.left, objectName, maxPropertyPathLength, allowComputed) || isMemberExpOf(exp.right, objectName, maxPropertyPathLength, allowComputed)); } /** * Returns whether the specified expression is a negation. * @param {Object} exp * @returns {boolean|undefined} */ function isNegationExpression(exp) { return exp && exp.type === 'UnaryExpression' && exp.operator === '!'; } /** * Returns whether the expression is a negation of a member of objectName, in the specified depth. * @param {Object} exp * @param {string} objectName * @param {number} maxPropertyPathLength * @returns {boolean|undefined} */ function isNegationOfMemberOf(exp, objectName, maxPropertyPathLength) { return isNegationExpression(exp) && isMemberExpOf(exp.argument, objectName, maxPropertyPathLength, false); } /** * * @param {Object} exp * @param {string} paramName * @returns {boolean|undefined} */ function isIdentifierOfParam(exp, paramName) { return exp && paramName && exp.type === 'Identifier' && exp.name === paramName; } /** * Returns the node of the value returned in the first line, if any * @param {Object} func * @returns {Object|null} */ function getValueReturnedInFirstLine(func) { var firstLine = getFirstFunctionLine(func); if (func) { if (isFunctionDefinitionWithBlock(func)) { return isReturnStatement(firstLine) ? firstLine.argument : null; } if (func.type === 'ArrowFunctionExpression') { return firstLine; } } return null; } /** * Returns whether the node is a call from the specified object name * @param {Object} node * @param {string} objName * @returns {boolean|undefined} */ function isCallFromObject(node, objName) { return node && node.type === 'CallExpression' && _.get(node, 'callee.object.name') === objName; } /** * Returns whether the node is actually computed (x['ab'] does not count, x['a' + 'b'] does * @param {Object} node * @returns {boolean|undefined} */ function isComputed(node) { return _.get(node, 'computed') && node.property.type !== 'Literal'; } /** * Returns whether the two expressions refer to the same object (e.g. a['b'].c and a.b.c) * @param {Object} a * @param {Object} b * @returns {boolean} */ function isEquivalentExp(a, b) { return _.isEqualWith(a, b, function (left, right, key) { if (_.includes(['loc', 'range', 'computed'], key)) { return true; } if (isComputed(left) || isComputed(right)) { return false; } if (key === 'property') { var leftValue = left.name || left.value; var rightValue = right.name || right.value; return leftValue === rightValue; } }); } /** * Returns whether the expression is a strict equality comparison, === * @param {Object} node * @returns {boolean} */ function isEqEqEq(node) { return node && node.type === 'BinaryExpression' && node.operator === '==='; } module.exports = { getCaller: getCaller, getMethodName: getMethodName, isMethodCall: isMethodCall, getFirstFunctionLine: getFirstFunctionLine, isMemberExpOf: isMemberExpOf, getFirstParamName: getFirstParamName, hasOnlyOneStatement: hasOnlyOneStatement, isObjectOfMethodCall: isObjectOfMethodCall, isEqEqEqToMemberOf: isBinaryExpWithMemberOf.bind(null, '==='), isNotEqEqToMemberOf: isBinaryExpWithMemberOf.bind(null, '!=='), isNegationOfParamMember: isNegationOfMemberOf, isIdentifierOfParam: isIdentifierOfParam, isNegationExpression: isNegationExpression, getValueReturnedInFirstLine: getValueReturnedInFirstLine, isCallFromObject: isCallFromObject, isComputed: isComputed, isEquivalentExp: isEquivalentExp, isEqEqEq: isEqEqEq };