escomplex-plugin-syntax-estree
Version:
Provides a plugin for typhonjs-escomplex module processing which loads syntax definitions for trait resolution for ESTree AST.
1,239 lines (1,055 loc) • 65.6 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _AbstractSyntaxLoader2 = require('typhonjs-escomplex-commons/dist/module/plugin/syntax/AbstractSyntaxLoader');
var _AbstractSyntaxLoader3 = _interopRequireDefault(_AbstractSyntaxLoader2);
var _ASTGenerator = require('typhonjs-escomplex-commons/dist/utils/ast/ASTGenerator');
var _ASTGenerator2 = _interopRequireDefault(_ASTGenerator);
var _HalsteadArray = require('typhonjs-escomplex-commons/dist/module/traits/HalsteadArray');
var _HalsteadArray2 = _interopRequireDefault(_HalsteadArray);
var _TraitUtil = require('typhonjs-escomplex-commons/dist/module/traits/TraitUtil');
var _TraitUtil2 = _interopRequireDefault(_TraitUtil);
var _actualize = require('typhonjs-escomplex-commons/dist/module/traits/actualize');
var _actualize2 = _interopRequireDefault(_actualize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Provides an typhonjs-escomplex-module / ESComplexModule plugin which loads syntax definitions for trait resolution
* for all ESTree AST nodes up to and including ES6.
*
* @see https://www.npmjs.com/package/typhonjs-escomplex-module
*/
var PluginSyntaxESTree = function (_AbstractSyntaxLoader) {
(0, _inherits3.default)(PluginSyntaxESTree, _AbstractSyntaxLoader);
function PluginSyntaxESTree() {
(0, _classCallCheck3.default)(this, PluginSyntaxESTree);
return (0, _possibleConstructorReturn3.default)(this, (PluginSyntaxESTree.__proto__ || (0, _getPrototypeOf2.default)(PluginSyntaxESTree)).apply(this, arguments));
}
(0, _createClass3.default)(PluginSyntaxESTree, [{
key: 'onConfigure',
// ESComplexModule plugin callbacks ------------------------------------------------------------------------------
/**
* Loads any default settings that are not already provided by any user options.
*
* @param {object} ev - escomplex plugin event data.
*
* The following options are:
* ```
* (boolean) commonjs - Boolean indicating that source code being processed is CommonJS; defaults to false.
*
* (function) dependencyResolver - Provides a function to resolve dynamic dependencies; defaults to undefined.
*
* (boolean|object) esmImportExport - As a boolean indicating whether ES Modules import / export statements
* contribute to modules logical SLOC & Halstead operators / operands; defaults
* to false. As an object separate boolean entries to set logical SLOC (lloc)
* and operators / operands (halstead) are available to set these parameters
* separately.
*
* (boolean) forin - Boolean indicating whether for...in / for...of loops should be considered a source of
* cyclomatic complexity; defaults to false.
*
* (boolean) logicalor - Boolean indicating whether operator || should be considered a source of cyclomatic
* complexity; defaults to true.
*
* (boolean) switchcase - Boolean indicating whether switch statements should be considered a source of cyclomatic
* complexity; defaults to true.
*
* (boolean|object) templateExpressions - As a boolean indicating whether template literal expressions
* contribute to logical SLOC & Halstead operators / operands; defaults
* to `true`. As an object separate boolean entries to set logical SLOC (lloc)
* and operators / operands (halstead) are available to set these parameters
* separately. When `lloc` is true tagged template expressions
* increment lloc by 1. If `halstead` is true then operators are added for '``'
* and each instance of a `${...}` expression.
* (boolean) trycatch - Boolean indicating whether catch clauses should be considered a source of cyclomatic
* complexity; defaults to false.
* ```
*/
value: function onConfigure(ev) {
ev.data.settings.commonjs = typeof ev.data.options.commonjs === 'boolean' ? ev.data.options.commonjs : false;
ev.data.settings.dependencyResolver = typeof ev.data.options.dependencyResolver === 'function' ? ev.data.options.dependencyResolver : void 0;
if ((0, _typeof3.default)(ev.data.options.esmImportExport) === 'object') {
ev.data.settings.esmImportExport = {
halstead: typeof ev.data.options.esmImportExport.halstead === 'boolean' ? ev.data.options.esmImportExport.halstead : false,
lloc: typeof ev.data.options.esmImportExport.lloc === 'boolean' ? ev.data.options.esmImportExport.lloc : false
};
} else {
// Catch all boolean to set `esmImportExport.halstead` & `esmImportExport.lloc` if a boolean is supplied.
var value = typeof ev.data.options.esmImportExport === 'boolean' ? ev.data.options.esmImportExport : false;
ev.data.settings.esmImportExport = { halstead: value, lloc: value };
}
ev.data.settings.forin = typeof ev.data.options.forin === 'boolean' ? ev.data.options.forin : false;
ev.data.settings.logicalor = typeof ev.data.options.logicalor === 'boolean' ? ev.data.options.logicalor : true;
ev.data.settings.switchcase = typeof ev.data.options.switchcase === 'boolean' ? ev.data.options.switchcase : true;
if ((0, _typeof3.default)(ev.data.options.templateExpression) === 'object') {
ev.data.settings.templateExpression = {
halstead: typeof ev.data.options.templateExpression.halstead === 'boolean' ? ev.data.options.templateExpression.halstead : true,
lloc: typeof ev.data.options.templateExpression.lloc === 'boolean' ? ev.data.options.templateExpression.lloc : true
};
} else {
// Catch all boolean to set `templateExpression.halstead` & `templateExpression.lloc` if a boolean is supplied.
var _value = typeof ev.data.options.templateExpression === 'boolean' ? ev.data.options.templateExpression : true;
ev.data.settings.templateExpression = { halstead: _value, lloc: _value };
}
ev.data.settings.trycatch = typeof ev.data.options.trycatch === 'boolean' ? ev.data.options.trycatch : false;
}
// Core / ES5 ESTree AST nodes -----------------------------------------------------------------------------------
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#arrayexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ArrayExpression',
value: function ArrayExpression() {
return (0, _actualize2.default)(0, 0, function (node) {
var operators = ['[]'];
if (Array.isArray(node.elements) && node.elements.length > 0) // Add length - 1 commas
{
operators.push.apply(operators, (0, _toConsumableArray3.default)(new Array(node.elements.length - 1).fill(',')));
}
return operators;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#assignmentexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'AssignmentExpression',
value: function AssignmentExpression() {
return (0, _actualize2.default)(function (node, parent) {
// Handle multiple assignment such as `a = b = c = 1` which resolves logical SLOC of 3.
// The top most `ExpressionStatement` already provides 1 lloc and `AssignmentExpression` for 'c' & '1' are
// additionally counted as additional lloc.
return parent && parent.type !== 'ExpressionStatement' ? 1 : 0;
}, 0, function (node) {
return node.operator;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#blockstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'BlockStatement',
value: function BlockStatement() {
return (0, _actualize2.default)(0, 0);
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#binaryexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'BinaryExpression',
value: function BinaryExpression() {
return (0, _actualize2.default)(0, 0, function (node) {
return node.operator;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#breakstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'BreakStatement',
value: function BreakStatement() {
return (0, _actualize2.default)(1, 0, 'break');
}
/**
* ES5 Node
*
* Processes CommonJS dependencies if settings.commonjs is set to true. An optional function
* settings.dependencyResolver may be used to resolve dynamic dependencies.
* @param {object} settings - escomplex settings
*
* @see https://github.com/estree/estree/blob/master/es5.md#callexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'CallExpression',
value: function CallExpression(settings) {
return (0, _actualize2.default)(function (node, parent) // lloc
{
// Handles nested CallExpression nodes. Since ExpressionStatement provides 1 lloc already the first
// CallExpression ignores posting an additional lloc; subsequent CallExpression nodes do contribute 1 lloc.
// Likewise the first CallExpression after a YieldExpression doesn't contribute 1 lloc.
return parent && parent.type !== 'ExpressionStatement' && parent.type !== 'YieldExpression' ? 1 : 0;
}, 0, // cyclomatic
'()', // operators
void 0, // operands
void 0, // ignoreKeys
void 0, // newScope
function (node) {
// Only process CJS dependencies if settings.commonjs is true.
if (settings.commonjs && node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments.length === 1) {
var dependency = node.arguments[0];
var dependencyPath = '* dynamic dependency *';
if (dependency.type === 'Literal' || dependency.type === 'StringLiteral') {
dependencyPath = typeof settings.dependencyResolver === 'function' ? settings.dependencyResolver(dependency.value) : dependency.value;
}
return { line: node.loc.start.line, path: dependencyPath, type: 'cjs' };
}
});
}
/**
* ES5 Node
* @param {object} settings - escomplex settings
* @see https://github.com/estree/estree/blob/master/es5.md#catchclause
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'CatchClause',
value: function CatchClause(settings) {
return (0, _actualize2.default)(1, function () {
return settings.trycatch ? 1 : 0;
}, 'catch');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#conditionalexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ConditionalExpression',
value: function ConditionalExpression() {
return (0, _actualize2.default)(0, 1, ':?');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#continuestatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ContinueStatement',
value: function ContinueStatement() {
return (0, _actualize2.default)(1, 0, 'continue');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#dowhilestatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'DoWhileStatement',
value: function DoWhileStatement() {
return (0, _actualize2.default)(2, function (node) {
return node.test ? 1 : 0;
}, 'dowhile');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#emptystatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'EmptyStatement',
value: function EmptyStatement() {
return (0, _actualize2.default)(0, 0);
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#expressionstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ExpressionStatement',
value: function ExpressionStatement() {
return (0, _actualize2.default)(function (node) {
// Ignore adding 1 lloc when the child expression is an ArrowFunctionExpression which is invalid / no-op.
return (0, _typeof3.default)(node.expression) === 'object' && node.expression.type !== 'ArrowFunctionExpression' ? 1 : 0;
}, 0);
}
/**
* ES5 Node
* @param {object} settings - escomplex settings
* @see https://github.com/estree/estree/blob/master/es5.md#forinstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ForInStatement',
value: function ForInStatement(settings) {
// return actualize(1, () => { return settings.forin ? 1 : 0; }, 'forin');
return (0, _actualize2.default)(1, function () {
return settings.forin ? 1 : 0;
}, function (node) {
var operators = ['forin'];
var childNodes = [];
if (node.left) {
childNodes.push(node.left);
}
if (node.right) {
childNodes.push(node.right);
}
if (childNodes.length > 0) {
operators.push.apply(operators, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parseNodes(childNodes).operators));
}
return operators;
}, function (node) {
var operands = [];
var childNodes = [];
if (node.left) {
childNodes.push(node.left);
}
if (node.right) {
childNodes.push(node.right);
}
if (childNodes.length > 0) {
operands.push.apply(operands, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parseNodes(childNodes).operands));
}
return operands;
}, ['left', 'right']);
}
/**
* ES5 Node
*
* Note: ASTGenerator is necessary to pick up operators / operands of `init, test, update` child nodes while
* excluding traversal / LLOC counts for these nodes.
*
* @see https://github.com/estree/estree/blob/master/es5.md#forstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ForStatement',
value: function ForStatement() {
return (0, _actualize2.default)(1, function (node) {
return node.test ? 1 : 0;
}, function (node) {
var operators = ['for'];
var childNodes = [];
if (node.init) {
childNodes.push(node.init);
}
if (node.test) {
childNodes.push(node.test);
}
if (node.update) {
childNodes.push(node.update);
}
if (childNodes.length > 0) {
operators.push.apply(operators, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parseNodes(childNodes).operators));
}
return operators;
}, function (node) {
var operands = [];
var childNodes = [];
if (node.init) {
childNodes.push(node.init);
}
if (node.test) {
childNodes.push(node.test);
}
if (node.update) {
childNodes.push(node.update);
}
if (childNodes.length > 0) {
operands.push.apply(operands, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parseNodes(childNodes).operands));
}
return operands;
}, ['init', 'test', 'update']);
}
/**
* ES5 Node
*
* Note: The function name (node.id) is returned as an operand and excluded from traversal as to not be included in
* the function operand calculations.
*
* @see https://github.com/estree/estree/blob/master/es5.md#functiondeclaration
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'FunctionDeclaration',
value: function FunctionDeclaration() {
return (0, _actualize2.default)(0, 0, void 0, void 0, ['id', 'params', 'defaults'], function (node, parent) {
var name = s_SAFE_COMPUTED_NAME(node, parent);
var operands = s_SAFE_COMPUTED_OPERANDS(node, parent);
var operators = [];
if (parent && parent.type === 'MethodDefinition') {
if (typeof node.generator === 'boolean' && node.generator) {
operators.push('function*');
}
if (typeof parent.computed === 'boolean' && parent.computed) {
operators.push.apply(operators, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parse(parent.key).operators));
}
} else {
operators.push(typeof node.generator === 'boolean' && node.generator ? 'function*' : 'function');
}
var paramNames = [];
// Parse params; must use ASTGenerator
if (Array.isArray(node.params)) {
node.params.forEach(function (param) {
var parsedParams = _ASTGenerator2.default.parse(param);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
// For param names only the left hand node of AssignmentPattern must be considered.
if (param.type === 'AssignmentPattern') {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parse(param.left).operands));
} else {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(parsedParams.operands));
}
});
}
// Esprima default values; param default values are stored in a non-standard `defaults` node.
if (Array.isArray(node.defaults)) {
node.defaults.forEach(function (value) {
if (value !== null && (typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) === 'object') {
var parsedParams = _ASTGenerator2.default.parse(value);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
operators.push('='); // Must also manually push '='
}
});
}
return {
type: 'method',
name: name,
cyclomatic: 1,
lineStart: node.loc.start.line,
lineEnd: node.loc.end.line,
lloc: 1,
operands: new _HalsteadArray2.default('operands', operands),
operators: new _HalsteadArray2.default('operators', operators),
paramNames: paramNames
};
});
}
// FunctionDeclaration()
// {
// return actualize(1, 0, (node, parent) =>
// {
// const operators = parent && parent.type === 'MethodDefinition' && typeof parent.computed === 'boolean' &&
// parent.computed ? [...ASTGenerator.parse(parent.key).operators] : [];
//
// if (parent && parent.type === 'MethodDefinition')
// {
// if (typeof node.generator === 'boolean' && node.generator)
// {
// operators.push('function*');
// }
// }
// else
// {
// operators.push(typeof node.generator === 'boolean' && node.generator ? 'function*' : 'function');
// }
//
// return operators;
// },
// (node, parent) => { return s_SAFE_COMPUTED_OPERANDS(node, parent); },
// 'id',
// (node, parent) =>
// {
// return {
// type: 'method',
// name: s_SAFE_COMPUTED_NAME(node, parent),
// lineStart: node.loc.start.line,
// lineEnd: node.loc.end.line,
// paramCount: node.params.length
// };
// });
// }
/**
* ES5 Node
*
* Note: The function name (node.id) is returned as an operand and excluded from traversal as to not be included in
* the function operand calculations.
*
* @see https://github.com/estree/estree/blob/master/es5.md#functionexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'FunctionExpression',
value: function FunctionExpression() {
return (0, _actualize2.default)(0, 0, void 0, void 0, ['id', 'params', 'defaults'], function (node, parent) {
var name = s_SAFE_COMPUTED_NAME(node, parent);
var operands = s_SAFE_COMPUTED_OPERANDS(node, parent);
var operators = [];
if (parent && parent.type === 'MethodDefinition') {
if (typeof node.generator === 'boolean' && node.generator) {
operators.push('function*');
}
if (typeof parent.computed === 'boolean' && parent.computed) {
operators.push.apply(operators, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parse(parent.key).operators));
}
} else {
operators.push(typeof node.generator === 'boolean' && node.generator ? 'function*' : 'function');
}
var paramNames = [];
// Parse params; must use ASTGenerator
if (Array.isArray(node.params)) {
node.params.forEach(function (param) {
var parsedParams = _ASTGenerator2.default.parse(param);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
// For param names only the left hand node of AssignmentPattern must be considered.
if (param.type === 'AssignmentPattern') {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parse(param.left).operands));
} else {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(parsedParams.operands));
}
});
}
// Esprima default values; param default values are stored in a non-standard `defaults` node.
if (Array.isArray(node.defaults)) {
node.defaults.forEach(function (value) {
if (value !== null && (typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) === 'object') {
var parsedParams = _ASTGenerator2.default.parse(value);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
operators.push('='); // Must also manually push '='
}
});
}
return {
type: 'method',
name: name,
cyclomatic: 1,
lineStart: node.loc.start.line,
lineEnd: node.loc.end.line,
lloc: 1,
operands: new _HalsteadArray2.default('operands', operands),
operators: new _HalsteadArray2.default('operators', operators),
paramNames: paramNames
};
});
}
// FunctionExpression()
// {
// return actualize(1, 0, (node, parent) =>
// {
// const operators = parent && parent.type === 'MethodDefinition' && typeof parent.computed === 'boolean' &&
// parent.computed ? [...ASTGenerator.parse(parent.key).operators] : [];
//
// if (parent && parent.type === 'MethodDefinition')
// {
// if (typeof node.generator === 'boolean' && node.generator)
// {
// operators.push('function*');
// }
// }
// else
// {
// operators.push(typeof node.generator === 'boolean' && node.generator ? 'function*' : 'function');
// }
//
// return operators;
// },
// (node, parent) =>
// {
// return s_SAFE_COMPUTED_OPERANDS(node, parent);
// },
// 'id',
// (node, parent) =>
// {
// return {
// type: 'method',
// name: s_SAFE_COMPUTED_NAME(node, parent),
// lineStart: node.loc.start.line,
// lineEnd: node.loc.end.line,
// paramCount: node.params.length
// };
// });
// }
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#identifier
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'Identifier',
value: function Identifier() {
return (0, _actualize2.default)(0, 0, void 0, function (node) {
return node.name;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#ifstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'IfStatement',
value: function IfStatement() {
return (0, _actualize2.default)(function (node) {
return node.alternate ? 2 : 1;
}, 1, ['if', { identifier: 'else', filter: function filter(node) {
return !!node.alternate;
} }]);
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#labeledstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'LabeledStatement',
value: function LabeledStatement() {
return (0, _actualize2.default)(0, 0);
}
/**
* ES5 Node
*
* Avoid conflicts between string literals and identifiers.
*
* @see https://github.com/estree/estree/blob/master/es5.md#literal
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'Literal',
value: function Literal() {
return (0, _actualize2.default)(0, 0, void 0, function (node) {
var operands = [];
if ((0, _typeof3.default)(node.regex) === 'object' && typeof node.raw !== 'undefined') {
operands.push(node.raw);
} else {
operands.push(typeof node.value === 'string' ? '"' + node.value + '"' : node.value);
}
return operands;
});
}
/**
* ES5 Node
* @param {object} settings - escomplex settings
* @see https://github.com/estree/estree/blob/master/es5.md#logicalexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'LogicalExpression',
value: function LogicalExpression(settings) {
return (0, _actualize2.default)(0, function (node) {
var isAnd = node.operator === '&&';
var isOr = node.operator === '||';
return isAnd || settings.logicalor && isOr ? 1 : 0;
}, function (node) {
return node.operator;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#memberexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'MemberExpression',
value: function MemberExpression() {
return (0, _actualize2.default)(0, 0, function (node) {
return typeof node.computed === 'boolean' && node.computed ? '[]' : '.';
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#newexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'NewExpression',
value: function NewExpression() {
return (0, _actualize2.default)(function (node) {
return node.callee.type === 'FunctionExpression' ? 1 : 0;
}, 0, 'new');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#objectexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ObjectExpression',
value: function ObjectExpression() {
return (0, _actualize2.default)(0, 0, '{}');
}
/**
* ES5 Node
*
* Note that w/ ES6+ `:` may be omitted and the Property node defines `shorthand` to indicate this case.
*
* @see https://github.com/estree/estree/blob/master/es5.md#property
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'Property',
value: function Property() {
return (0, _actualize2.default)(1, 0, function (node) {
return typeof node.shorthand === 'undefined' ? ':' : typeof node.shorthand === 'boolean' && !node.shorthand ? ':' : void 0;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#returnstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ReturnStatement',
value: function ReturnStatement() {
return (0, _actualize2.default)(1, 0, 'return');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#sequenceexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'SequenceExpression',
value: function SequenceExpression() {
return (0, _actualize2.default)(0, 0);
}
/**
* ES5 Node
* @param {object} settings - escomplex settings
* @see https://github.com/estree/estree/blob/master/es5.md#switchcase
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'SwitchCase',
value: function SwitchCase(settings) {
return (0, _actualize2.default)(1, function (node) {
return settings.switchcase && node.test ? 1 : 0;
}, function (node) {
return node.test ? 'case' : 'default';
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#switchstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'SwitchStatement',
value: function SwitchStatement() {
return (0, _actualize2.default)(1, 0, 'switch');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#thisexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ThisExpression',
value: function ThisExpression() {
return (0, _actualize2.default)(0, 0, 'this');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#throwstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ThrowStatement',
value: function ThrowStatement() {
return (0, _actualize2.default)(1, 0, 'throw');
}
/**
* ES5 Node
*
* Note: esprima has duplicate nodes the catch block; `handler` is the actual ESTree spec.
*
* @see https://github.com/estree/estree/blob/master/es5.md#trystatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'TryStatement',
value: function TryStatement() {
return (0, _actualize2.default)(1, 0, function (node) {
return node.finalizer ? ['try', 'finally'] : ['try'];
}, void 0, ['guardedHandlers', 'handlers']);
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#unaryexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'UnaryExpression',
value: function UnaryExpression() {
return (0, _actualize2.default)(0, 0, function (node) {
return node.operator + ' (' + (node.prefix ? 'pre' : 'post') + 'fix)';
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#updateexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'UpdateExpression',
value: function UpdateExpression() {
return (0, _actualize2.default)(0, 0, function (node) {
return node.operator + ' (' + (node.prefix ? 'pre' : 'post') + 'fix)';
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#variabledeclaration
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'VariableDeclaration',
value: function VariableDeclaration() {
return (0, _actualize2.default)(0, 0, function (node) {
return node.kind;
});
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#variabledeclarator
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'VariableDeclarator',
value: function VariableDeclarator() {
return (0, _actualize2.default)(1, 0, { identifier: '=', filter: function filter(node) {
return !!node.init;
} });
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#whilestatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'WhileStatement',
value: function WhileStatement() {
return (0, _actualize2.default)(1, function (node) {
return node.test ? 1 : 0;
}, 'while');
}
/**
* ES5 Node
* @see https://github.com/estree/estree/blob/master/es5.md#withstatement
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'WithStatement',
value: function WithStatement() {
return (0, _actualize2.default)(1, 0, 'with');
}
// ES6 ESTree AST nodes ------------------------------------------------------------------------------------------
/**
* ES6 Node
* @see https://github.com/estree/estree/blob/master/es2015.md#assignmentpattern
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'AssignmentPattern',
value: function AssignmentPattern() {
return (0, _actualize2.default)(1, 0, function (node) {
return node.operator;
});
}
/**
* ES6 Node
* @see https://github.com/estree/estree/blob/master/es2015.md#arraypattern
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ArrayPattern',
value: function ArrayPattern() {
return (0, _actualize2.default)(0, 0, function (node) {
var operators = ['[]'];
if (Array.isArray(node.elements) && node.elements.length > 0) // Add length - 1 commas
{
operators.push.apply(operators, (0, _toConsumableArray3.default)(new Array(node.elements.length - 1).fill(',')));
}
return operators;
});
}
/**
* ES6 Node
*
* Note: If the parent node is an ExpressionStatement the arrow function is essentially a no-op / invalid. In this
* case no metrics are gathered, but a method is still registered with no data. Further analysis looking for methods
* with no logical SLOC will reveal this case.
*
* @see https://github.com/estree/estree/blob/master/es2015.md#arrowfunctionexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ArrowFunctionExpression',
value: function ArrowFunctionExpression() {
return (0, _actualize2.default)(0, 0, void 0, void 0, function (node, parent) {
// If not valid do not traverse any children.
return parent && parent.type !== 'ExpressionStatement' ? ['id', 'params', 'defaults'] : null;
}, function (node, parent) {
var isValid = parent && parent.type !== 'ExpressionStatement';
var name = _TraitUtil2.default.safeName(node.id);
var operands = [];
var operators = isValid ? ['function=>'] : [];
var paramNames = [];
// Parse params; must use ASTGenerator
if (isValid && Array.isArray(node.params)) {
node.params.forEach(function (param) {
var parsedParams = _ASTGenerator2.default.parse(param);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
// For param names only the left hand node of AssignmentPattern must be considered.
if (param.type === 'AssignmentPattern') {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(_ASTGenerator2.default.parse(param.left).operands));
} else {
paramNames.push.apply(paramNames, (0, _toConsumableArray3.default)(parsedParams.operands));
}
});
}
// Esprima default values; param default values are stored in a non-standard `defaults` node.
if (isValid && Array.isArray(node.defaults)) {
node.defaults.forEach(function (value) {
if (value !== null && (typeof value === 'undefined' ? 'undefined' : (0, _typeof3.default)(value)) === 'object') {
var parsedParams = _ASTGenerator2.default.parse(value);
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsedParams.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsedParams.operators));
operators.push('='); // Must also manually push '='
}
});
}
return {
type: 'method',
name: name,
cyclomatic: isValid ? 1 : 0,
lineStart: node.loc.start.line,
lineEnd: node.loc.end.line,
lloc: isValid ? 1 : 0,
operands: isValid ? new _HalsteadArray2.default('operands', operands) : void 0,
operators: isValid ? new _HalsteadArray2.default('operators', operators) : void 0,
paramNames: paramNames,
// ArrowFunctionExpressions without brackets have an implicit `return` expression so post increment lloc.
postLloc: isValid && (0, _typeof3.default)(node.body) === 'object' && node.body.type !== 'BlockStatement' ? 1 : 0
// NOTE (altered 12/8/18): ESTree has expression entry which was removed in Babel Parser / 7.0 so
// node.body.type must be checked to determine if an expression or block / return statement.
// Reference: https://github.com/babel/babel/issues/6773
// postLloc: isValid && typeof node.expression === 'boolean' && node.expression ? 1 : 0
};
});
}
/**
* ES6 Node
* @see https://github.com/estree/estree/blob/master/es2015.md#classbody
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ClassBody',
value: function ClassBody() {
return (0, _actualize2.default)(0, 0);
}
// /**
// * ES6 Node
// * @see https://github.com/estree/estree/blob/master/es2015.md#classdeclaration
// * @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
// */
// ClassDeclaration()
// {
// return actualize(1, 0, 'class', void 0, void 0, (node) =>
// {
// return {
// type: 'class',
// name: TraitUtil.safeName(node.id),
// lineStart: node.loc.start.line,
// lineEnd: node.loc.end.line
// };
// });
// }
//
// /**
// * ES6 Node
// * @see https://github.com/estree/estree/blob/master/es2015.md#classexpression
// * @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
// */
// ClassExpression()
// {
// return actualize(1, 0, 'class', void 0, void 0, (node) =>
// {
// return {
// type: 'class',
// name: TraitUtil.safeName(node.id),
// lineStart: node.loc.start.line,
// lineEnd: node.loc.end.line
// };
// });
// }
/**
* ES6 Node
* @see https://github.com/estree/estree/blob/master/es2015.md#classdeclaration
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ClassDeclaration',
value: function ClassDeclaration() {
return (0, _actualize2.default)(0, 0, void 0, void 0, ['id', 'superClass'], function (node) {
var name = _TraitUtil2.default.safeName(node.id);
var operands = name !== '<anonymous>' ? [name] : [];
var operators = ['class'];
var superClassName = void 0;
if (typeof node.superClass !== 'undefined' && node.superClass !== null) {
var parsed = _ASTGenerator2.default.parse(node.superClass);
operators.push('extends');
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsed.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsed.operators));
// The following will pick up a Identifier or a computed value (string); computed expressions return
// `<computed~expression>`.
superClassName = node.superClass.type === 'Identifier' ? _TraitUtil2.default.safeName(node.superClass) : '<computed~' + parsed.source + '>';
}
return {
type: 'class',
name: name,
superClassName: superClassName,
lineStart: node.loc.start.line,
lineEnd: node.loc.end.line,
lloc: 1,
operands: new _HalsteadArray2.default('operands', operands),
operators: new _HalsteadArray2.default('operators', operators)
};
});
}
/**
* ES6 Node
* @see https://github.com/estree/estree/blob/master/es2015.md#classexpression
* @returns {{lloc: *, cyclomatic: *, operators: *, operands: *, ignoreKeys: *, newScope: *, dependencies: *}}
*/
}, {
key: 'ClassExpression',
value: function ClassExpression() {
return (0, _actualize2.default)(0, 0, void 0, void 0, ['id', 'superClass'], function (node) {
var name = _TraitUtil2.default.safeName(node.id);
var operands = name !== '<anonymous>' ? [name] : [];
var operators = ['class'];
var superClassName = void 0;
if (typeof node.superClass !== 'undefined' && node.superClass !== null) {
var parsed = _ASTGenerator2.default.parse(node.superClass);
operators.push('extends');
operands.push.apply(operands, (0, _toConsumableArray3.default)(parsed.operands));
operators.push.apply(operators, (0, _toConsumableArray3.default)(parsed.operators));
// The following will pick up a Identifier or a computed value (string); computed expressions return
// `<computed~expression>`.
superClassName = node.superClass.type === 'Identifier' ? _TraitUtil2.default.safeName(node.superClass) : '<computed~' + parsed.source + '>';
}
return {
type: 'class',
name: _TraitUtil2.default.safeName(node.id),
superClassName: superClassName,
lineStart: node.loc.start.line,
lineEnd: node.loc.end.line,
lloc: 1,
operands: new _HalsteadArray2.default('operands', operands),
operators: new _HalsteadArray2.default('operators', operators)
};
});
}
/**
* ES6 Node
* @param {object} settings - Runtime settings of escomplex.
* @see https://github.com/estree/estree/blob/m