complexity-report
Advanced tools
+2
-2
| { | ||
| "name": "complexity-report", | ||
| "version": "0.2.8", | ||
| "version": "0.3.0", | ||
| "author": "Phil Booth <pmbooth@gmail.com>", | ||
@@ -35,3 +35,3 @@ "description": "A tool for reporting code complexity metrics in JavaScript projects.", | ||
| "jshint": "0.9.x", | ||
| "mocha": "1.6.x", | ||
| "mocha": "1.7.x", | ||
| "chai": "1.3.x" | ||
@@ -38,0 +38,0 @@ }, |
+361
-319
@@ -5,391 +5,433 @@ /** | ||
| /*jshint globalstrict:true */ | ||
| /*globals exports, require */ | ||
| (function () { | ||
| 'use strict'; | ||
| 'use strict'; | ||
| var check, esprima, syntaxDefinitions, report, operators, operands; | ||
| var check, esprima, syntaxDefinitions, report; | ||
| exports.run = run; | ||
| exports.run = run; | ||
| check = require('check-types'); | ||
| esprima = require('esprima'); | ||
| check = require('check-types'); | ||
| esprima = require('esprima'); | ||
| /** | ||
| * Public function `run`. | ||
| * | ||
| * Returns a an object detailing the complexity of the code in the | ||
| * source argument. | ||
| * | ||
| * @param source {string} The source code to analyse for complexity. | ||
| * @param [options] {object} Options to modify the complexity calculation. | ||
| * | ||
| */ | ||
| function run (source, options) { | ||
| var settings, ast; | ||
| /** | ||
| * Public function `run`. | ||
| * | ||
| * Returns a an object detailing the complexity of the code in the | ||
| * source argument. | ||
| * | ||
| * @param source {string} The source code to analyse for complexity. | ||
| * @param [options] {object} Options to modify the complexity calculation. | ||
| * | ||
| */ | ||
| function run (source, options) { | ||
| var settings, ast; | ||
| check.verifyUnemptyString(source, 'Invalid source'); | ||
| check.verifyUnemptyString(source, 'Invalid source'); | ||
| if (check.isObject(options)) { | ||
| settings = options; | ||
| } else { | ||
| settings = getDefaultSettings(); | ||
| } | ||
| if (check.isObject(options)) { | ||
| settings = options; | ||
| } else { | ||
| settings = getDefaultSettings(); | ||
| } | ||
| syntaxDefinitions = getSyntaxDefinitions(settings); | ||
| report = createReport(); | ||
| operators = {}; | ||
| operands = {}; | ||
| syntaxDefinitions = getSyntaxDefinitions(settings); | ||
| report = createReport(); | ||
| ast = esprima.parse(source, { | ||
| loc: true | ||
| }); | ||
| ast = esprima.parse(source, { | ||
| loc: true | ||
| }); | ||
| processTree(ast.body, undefined, undefined, {}, {}); | ||
| processTree(ast.body, undefined, undefined); | ||
| return report; | ||
| } | ||
| calculateHalsteadMetrics(); | ||
| function getDefaultSettings () { | ||
| return { | ||
| logicalor: true, | ||
| switchcase: true, | ||
| forin: false, | ||
| trycatch: false | ||
| }; | ||
| } | ||
| return report; | ||
| } | ||
| function getSyntaxDefinitions (settings) { | ||
| // TODO: Refactor to traits. | ||
| return { | ||
| Literal: { | ||
| operands: [ { | ||
| name: function (node) { | ||
| if (check.isString(node.value)) { | ||
| // Avoid conflicts between string literals and identifiers. | ||
| return '"' + node.value + '"'; | ||
| } | ||
| function getDefaultSettings () { | ||
| return { | ||
| logicalor: true, | ||
| switchcase: true, | ||
| forin: false, | ||
| trycatch: false | ||
| }; | ||
| } | ||
| return node.value; | ||
| function getSyntaxDefinitions (settings) { | ||
| // TODO: Refactor to traits. | ||
| return { | ||
| Literal: { | ||
| operands: [ { | ||
| identifier: function (node) { | ||
| if (check.isString(node.value)) { | ||
| // Avoid conflicts between string literals and identifiers. | ||
| return '"' + node.value + '"'; | ||
| } | ||
| } ] | ||
| return node.value; | ||
| } | ||
| } ] | ||
| }, | ||
| Identifier: { | ||
| operands: [ { identifier: function (node) { return node.name; } } ] | ||
| }, | ||
| BlockStatement: { | ||
| children: [ 'body' ] | ||
| }, | ||
| ObjectExpression: { | ||
| operands: [ { identifier: safeName } ], | ||
| children: [ 'properties' ] | ||
| }, | ||
| Property: { | ||
| operators: [ { identifier: ':' } ], | ||
| children: [ 'key', 'value' ], | ||
| getAssignedName: function (node) { return safeName(node.key); } | ||
| }, | ||
| ThisExpression: { | ||
| operands: [ { identifier: 'this' } ] | ||
| }, | ||
| MemberExpression: { | ||
| operators: [ { identifier: '.' } ], | ||
| children: [ 'object', 'property' ] | ||
| }, | ||
| CallExpression: getFunctionCallSyntaxDefinition('()'), | ||
| NewExpression: getFunctionCallSyntaxDefinition('new'), | ||
| ExpressionStatement: { | ||
| children: [ 'expression' ] | ||
| }, | ||
| VariableDeclaration: { | ||
| operators: [ { identifier: function (node) { return node.kind; } } ], | ||
| children: [ 'declarations' ] | ||
| }, | ||
| VariableDeclarator: { | ||
| operators: [ { | ||
| identifier: '=', | ||
| optional: function (node) { | ||
| return !!node.init; | ||
| } | ||
| } ], | ||
| children: [ 'id', 'init' ], | ||
| getAssignedName: function (node) { return safeName(node.id); } | ||
| }, | ||
| AssignmentExpression: { | ||
| operators: [ { identifier: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ], | ||
| getAssignedName: function (node) { | ||
| if (node.left.type === 'MemberExpression') { | ||
| return safeName(node.left.object) + '.' + node.left.property.name; | ||
| } | ||
| return safeName(node.left.id); | ||
| } | ||
| }, | ||
| BinaryExpression: { | ||
| operators: [ { identifier: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ] | ||
| }, | ||
| LogicalExpression: { | ||
| complexity: function (node) { | ||
| return settings.logicalor && node.operator === '||'; | ||
| }, | ||
| Identifier: { | ||
| operands: [ { name: function (node) { return node.name; } } ] | ||
| }, | ||
| BlockStatement: { | ||
| children: [ 'body' ] | ||
| }, | ||
| ObjectExpression: { | ||
| operands: [ { name: safeName } ], | ||
| children: [ 'properties' ] | ||
| }, | ||
| Property: { | ||
| operators: [ { name: ':' } ], | ||
| children: [ 'key', 'value' ], | ||
| getAssignedName: function (node) { return safeName(node.key); } | ||
| }, | ||
| ThisExpression: { | ||
| operands: [ { name: 'this' } ] | ||
| }, | ||
| MemberExpression: { | ||
| operators: [ { name: function (node) { return '.'; } } ], | ||
| children: [ 'object', 'property' ] | ||
| }, | ||
| CallExpression: getFunctionCallSyntaxDefinition('()'), | ||
| NewExpression: getFunctionCallSyntaxDefinition('new'), | ||
| ExpressionStatement: { | ||
| children: [ 'expression' ] | ||
| }, | ||
| VariableDeclaration: { | ||
| operators: [ { name: function (node) { return node.kind; } } ], | ||
| children: [ 'declarations' ] | ||
| }, | ||
| VariableDeclarator: { | ||
| operators: [ { name: '=', optional: function (node) { return !!node.init; } } ], | ||
| children: [ 'id', 'init' ], | ||
| getAssignedName: function (node) { return safeName(node.id); } | ||
| }, | ||
| AssignmentExpression: { | ||
| operators: [ { name: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ], | ||
| getAssignedName: function (node) { | ||
| if (node.left.type === 'MemberExpression') { | ||
| return safeName(node.left.object) + '.' + node.left.property.name; | ||
| } | ||
| return safeName(node.left.id); | ||
| operators: [ { identifier: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ] | ||
| }, | ||
| IfStatement: { | ||
| complexity: true, | ||
| operators: [ { | ||
| identifier: 'if' | ||
| }, { | ||
| identifier: 'else', | ||
| optional: function (node) { return !!node.alternate; } | ||
| } ], | ||
| children: [ 'test', 'consequent', 'alternate' ] | ||
| }, | ||
| ConditionalExpression: { | ||
| complexity: true, | ||
| operators: [ { identifier: ':?' } ], | ||
| children: [ 'test', 'consequent', 'alternate' ] | ||
| }, | ||
| SwitchStatement: { | ||
| operators: [ { identifier: 'switch' } ], | ||
| children: [ 'discriminant', 'cases' ] | ||
| }, | ||
| SwitchCase: { | ||
| complexity: function (node) { return settings.switchcase && node.test; }, | ||
| operators: [ { | ||
| identifier: function (node) { | ||
| return node.test ? 'case' : 'default'; | ||
| } | ||
| }, | ||
| BinaryExpression: { | ||
| operators: [ { name: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ] | ||
| }, | ||
| LogicalExpression: { | ||
| complexity: function (node) { return settings.logicalor && node.operator === '||'; }, | ||
| operators: [ { name: function (node) { return node.operator; } } ], | ||
| children: [ 'left', 'right' ] | ||
| }, | ||
| IfStatement: { | ||
| complexity: true, | ||
| operators: [ | ||
| { name: 'if' }, | ||
| { name: 'else', optional: function (node) { return !!node.alternate; } } | ||
| ], | ||
| children: [ 'test', 'consequent', 'alternate' ] | ||
| }, | ||
| ConditionalExpression: { | ||
| complexity: true, | ||
| operators: [ { name: ':?' } ], | ||
| children: [ 'test', 'consequent', 'alternate' ] | ||
| }, | ||
| SwitchStatement: { | ||
| operators: [ { name: 'switch' } ], | ||
| children: [ 'discriminant', 'cases' ] | ||
| }, | ||
| SwitchCase: { | ||
| complexity: function (node) { return settings.switchcase && node.test; }, | ||
| operators: [ { name: function (node) { return node.test ? 'case' : 'default'; } } ], | ||
| children: [ 'test', 'consequent' ] | ||
| }, | ||
| BreakStatement: { | ||
| operators: [ { name: 'break' } ] | ||
| }, | ||
| ForStatement: getLoopSyntaxDefinition('for'), | ||
| ForInStatement: { | ||
| complexity: function (node) { return settings.forin; }, | ||
| operators: [ { name: 'forin' } ], | ||
| children: [ 'left', 'right', 'body' ] | ||
| }, | ||
| WhileStatement: getLoopSyntaxDefinition('while'), | ||
| DoWhileStatement: getLoopSyntaxDefinition('dowhile'), | ||
| FunctionDeclaration: getFunctionSyntaxDefinition(), | ||
| FunctionExpression: getFunctionSyntaxDefinition(), | ||
| ReturnStatement: { | ||
| operators: [ { name: 'return' } ], | ||
| children: [ 'argument' ] | ||
| }, | ||
| TryStatement: { | ||
| children: [ 'block', 'handlers' ] | ||
| }, | ||
| CatchClause: { | ||
| complexity: function (node) { return settings.trycatch; }, | ||
| operators: [ { name: 'catch' } ], | ||
| children: [ 'param', 'body' ] | ||
| }, | ||
| ThrowStatement: { | ||
| operators: [ { name: 'throw' } ], | ||
| children: [ 'argument' ] | ||
| } | ||
| }; | ||
| } | ||
| function safeName (object, defaultName) { | ||
| if (check.isObject(object) && check.isUnemptyString(object.name)) { | ||
| return object.name; | ||
| } ], | ||
| children: [ 'test', 'consequent' ] | ||
| }, | ||
| BreakStatement: { | ||
| operators: [ { identifier: 'break' } ] | ||
| }, | ||
| ForStatement: getLoopSyntaxDefinition('for'), | ||
| ForInStatement: { | ||
| complexity: function (node) { return settings.forin; }, | ||
| operators: [ { identifier: 'forin' } ], | ||
| children: [ 'left', 'right', 'body' ] | ||
| }, | ||
| WhileStatement: getLoopSyntaxDefinition('while'), | ||
| DoWhileStatement: getLoopSyntaxDefinition('dowhile'), | ||
| FunctionDeclaration: getFunctionSyntaxDefinition(), | ||
| FunctionExpression: getFunctionSyntaxDefinition(), | ||
| ReturnStatement: { | ||
| operators: [ { identifier: 'return' } ], | ||
| children: [ 'argument' ] | ||
| }, | ||
| TryStatement: { | ||
| children: [ 'block', 'handlers' ] | ||
| }, | ||
| CatchClause: { | ||
| complexity: function (node) { return settings.trycatch; }, | ||
| operators: [ { identifier: 'catch' } ], | ||
| children: [ 'param', 'body' ] | ||
| }, | ||
| ThrowStatement: { | ||
| operators: [ { identifier: 'throw' } ], | ||
| children: [ 'argument' ] | ||
| } | ||
| }; | ||
| } | ||
| if (check.isUnemptyString(defaultName)) { | ||
| return defaultName; | ||
| } | ||
| return '<anonymous>'; | ||
| function safeName (object, defaultName) { | ||
| if (check.isObject(object) && check.isUnemptyString(object.name)) { | ||
| return object.name; | ||
| } | ||
| function getFunctionCallSyntaxDefinition (type) { | ||
| return { | ||
| operators: [ { name: type } ], | ||
| children: [ 'arguments', 'callee' ] | ||
| }; | ||
| if (check.isUnemptyString(defaultName)) { | ||
| return defaultName; | ||
| } | ||
| function getLoopSyntaxDefinition (type) { | ||
| return { | ||
| complexity: function (node) { return !!node.test; }, | ||
| operators: [ { name: type } ], | ||
| children: [ 'init', 'test', 'update', 'body' ] | ||
| }; | ||
| } | ||
| return '<anonymous>'; | ||
| } | ||
| function getFunctionSyntaxDefinition () { | ||
| return { | ||
| operators: [ { name: 'function' } ], | ||
| operands: [ { name: function (node) { return safeName(node.id); } } ], | ||
| children: [ 'params', 'body' ], | ||
| isFunction: true | ||
| }; | ||
| } | ||
| function getFunctionCallSyntaxDefinition (type) { | ||
| return { | ||
| operators: [ { identifier: type } ], | ||
| children: [ 'arguments', 'callee' ] | ||
| }; | ||
| } | ||
| function createReport () { | ||
| return { | ||
| aggregate: createFunctionReport(), | ||
| functions: [] | ||
| }; | ||
| } | ||
| function getLoopSyntaxDefinition (type) { | ||
| return { | ||
| complexity: function (node) { return !!node.test; }, | ||
| operators: [ { identifier: type } ], | ||
| children: [ 'init', 'test', 'update', 'body' ] | ||
| }; | ||
| } | ||
| function createFunctionReport (name, lines) { | ||
| return { | ||
| name: name, | ||
| lines: lines, | ||
| complexity: { | ||
| cyclomatic: 1, | ||
| halstead: createInitialHalsteadState() | ||
| } | ||
| }; | ||
| } | ||
| function getFunctionSyntaxDefinition () { | ||
| return { | ||
| operators: [ { identifier: 'function' } ], | ||
| operands: [ { identifier: function (node) { return safeName(node.id); } } ], | ||
| children: [ 'params', 'body' ], | ||
| isFunction: true | ||
| }; | ||
| } | ||
| function createInitialHalsteadState () { | ||
| return { | ||
| operators: createInitialHalsteadItemState(), | ||
| operands: createInitialHalsteadItemState() | ||
| }; | ||
| } | ||
| function createReport () { | ||
| return { | ||
| aggregate: createFunctionReport(), | ||
| functions: [] | ||
| }; | ||
| } | ||
| function createInitialHalsteadItemState () { | ||
| return { | ||
| distinct: 0, | ||
| total: 0 | ||
| }; | ||
| } | ||
| function createFunctionReport (name, lines) { | ||
| return { | ||
| name: name, | ||
| lines: lines, | ||
| complexity: { | ||
| cyclomatic: 1, | ||
| halstead: createInitialHalsteadState() | ||
| } | ||
| }; | ||
| } | ||
| function processTree (tree, assignedName, currentReport, currentOperators, currentOperands) { | ||
| var i; | ||
| function createInitialHalsteadState () { | ||
| return { | ||
| operators: createInitialHalsteadItemState(), | ||
| operands: createInitialHalsteadItemState() | ||
| }; | ||
| } | ||
| check.verifyArray(tree, 'Invalid syntax tree'); | ||
| function createInitialHalsteadItemState () { | ||
| return { | ||
| distinct: 0, | ||
| total: 0, | ||
| identifiers: [] | ||
| }; | ||
| } | ||
| for (i = 0; i < tree.length; i += 1) { | ||
| processNode(tree[i], assignedName, currentReport, currentOperators, currentOperands); | ||
| } | ||
| function processTree (tree, assignedName, currentReport) { | ||
| var i; | ||
| check.verifyArray(tree, 'Invalid syntax tree'); | ||
| for (i = 0; i < tree.length; i += 1) { | ||
| processNode(tree[i], assignedName, currentReport); | ||
| } | ||
| } | ||
| function processNode (node, assignedName, currentReport, currentOperators, currentOperands) { | ||
| var def; | ||
| function processNode (node, assignedName, currentReport) { | ||
| var def; | ||
| if (check.isObject(node)) { | ||
| def = syntaxDefinitions[node.type]; | ||
| if (check.isObject(node)) { | ||
| def = syntaxDefinitions[node.type]; | ||
| if (check.isObject(syntaxDefinitions[node.type])) { | ||
| processComplexity(node, currentReport); | ||
| processOperators(node, currentReport); | ||
| processOperands(node, currentReport); | ||
| if (check.isObject(syntaxDefinitions[node.type])) { | ||
| processComplexity(node, currentReport); | ||
| processOperators(node, currentOperators, currentReport); | ||
| processOperands(node, currentOperands, currentReport); | ||
| if (def.isFunction) { | ||
| processChildrenInNewScope(node, assignedName); | ||
| } else { | ||
| processChildren(node, currentReport, currentOperators, currentOperands); | ||
| } | ||
| if (def.isFunction) { | ||
| processChildrenInNewScope(node, assignedName); | ||
| } else { | ||
| processChildren(node, currentReport); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| function processComplexity (node, currentReport) { | ||
| var def = syntaxDefinitions[node.type]; | ||
| function processComplexity (node, currentReport) { | ||
| var def = syntaxDefinitions[node.type]; | ||
| if ( | ||
| def.complexity === true || | ||
| ( | ||
| check.isFunction(def.complexity) && | ||
| def.complexity(node) | ||
| ) | ||
| ) { | ||
| incrementComplexity(currentReport); | ||
| } | ||
| if ( | ||
| def.complexity === true || | ||
| ( | ||
| check.isFunction(def.complexity) && | ||
| def.complexity(node) | ||
| ) | ||
| ) { | ||
| incrementComplexity(currentReport); | ||
| } | ||
| } | ||
| function incrementComplexity (currentReport) { | ||
| report.aggregate.complexity.cyclomatic += 1; | ||
| function incrementComplexity (currentReport) { | ||
| report.aggregate.complexity.cyclomatic += 1; | ||
| if (currentReport) { | ||
| currentReport.complexity.cyclomatic += 1; | ||
| } | ||
| if (currentReport) { | ||
| currentReport.complexity.cyclomatic += 1; | ||
| } | ||
| } | ||
| function processOperators (node, currentOperators, currentReport) { | ||
| processHalsteadMetric(node, 'operators', operators, currentOperators, currentReport); | ||
| } | ||
| function processOperators (node, currentReport) { | ||
| processHalsteadMetric(node, 'operators', currentReport); | ||
| } | ||
| function processOperands (node, currentOperands, currentReport) { | ||
| processHalsteadMetric(node, 'operands', operands, currentOperands, currentReport); | ||
| } | ||
| function processOperands (node, currentReport) { | ||
| processHalsteadMetric(node, 'operands', currentReport); | ||
| } | ||
| function processHalsteadMetric (node, metric, items, currentItems, currentReport) { | ||
| var def = syntaxDefinitions[node.type], i, name; | ||
| function processHalsteadMetric (node, metric, currentReport) { | ||
| var def = syntaxDefinitions[node.type], i, identifier; | ||
| if (check.isArray(def[metric])) { | ||
| for (i = 0; i < def[metric].length; i += 1) { | ||
| if (check.isFunction(def[metric][i].name)) { | ||
| name = def[metric][i].name(node); | ||
| } else { | ||
| name = def[metric][i].name; | ||
| } | ||
| if (check.isArray(def[metric])) { | ||
| for (i = 0; i < def[metric].length; i += 1) { | ||
| if (check.isFunction(def[metric][i].identifier)) { | ||
| identifier = def[metric][i].identifier(node); | ||
| } else { | ||
| identifier = def[metric][i].identifier; | ||
| } | ||
| if ( | ||
| check.isFunction(def[metric][i].optional) === false || | ||
| def[metric][i].optional(node) === true | ||
| ) { | ||
| halsteadItemEncountered(name, currentItems, items, currentReport, metric); | ||
| } | ||
| if ( | ||
| check.isFunction(def[metric][i].optional) === false || | ||
| def[metric][i].optional(node) === true | ||
| ) { | ||
| halsteadItemEncountered(currentReport, metric, identifier); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| function halsteadItemEncountered (name, currentItems, items, currentReport, metric) { | ||
| incrementHalsteadItems(name, currentItems, currentReport, metric); | ||
| incrementHalsteadItems(name, items, report.aggregate, metric); | ||
| function halsteadItemEncountered (currentReport, metric, identifier) { | ||
| if (currentReport) { | ||
| incrementHalsteadItems(currentReport, metric, identifier); | ||
| } | ||
| function incrementHalsteadItems (name, currentItems, baseReport, metric) { | ||
| incrementDistinctHalsteadItems(name, currentItems, baseReport, metric); | ||
| incrementTotalHalsteadItems(baseReport, metric); | ||
| } | ||
| incrementHalsteadItems(report.aggregate, metric, identifier); | ||
| } | ||
| function incrementDistinctHalsteadItems (name, currentItems, baseReport, metric) { | ||
| if (Object.prototype.hasOwnProperty(name)) { | ||
| // Avoid clashes with built-in property names. | ||
| incrementDistinctHalsteadItems('_' + name, currentItems, baseReport, metric); | ||
| } else if (!currentItems[name]) { | ||
| incrementHalsteadMetric(baseReport, metric, 'distinct'); | ||
| currentItems[name] = true; | ||
| } | ||
| } | ||
| function incrementHalsteadItems (baseReport, metric, identifier) { | ||
| incrementDistinctHalsteadItems(baseReport, metric, identifier); | ||
| incrementTotalHalsteadItems(baseReport, metric); | ||
| } | ||
| function incrementTotalHalsteadItems (baseReport, metric) { | ||
| incrementHalsteadMetric(baseReport, metric, 'total'); | ||
| function incrementDistinctHalsteadItems (baseReport, metric, identifier) { | ||
| if (Object.prototype.hasOwnProperty(identifier)) { | ||
| // Avoid clashes with built-in property names. | ||
| incrementDistinctHalsteadItems(baseReport, metric, '_' + identifier); | ||
| } else if (isHalsteadMetricDistinct(baseReport, metric, identifier)) { | ||
| recordDistinctHalsteadMetric(baseReport, metric, identifier); | ||
| incrementHalsteadMetric(baseReport, metric, 'distinct'); | ||
| } | ||
| } | ||
| function incrementHalsteadMetric (baseReport, metric, type) { | ||
| if (baseReport) { | ||
| baseReport.complexity.halstead[metric][type] += 1; | ||
| } | ||
| function isHalsteadMetricDistinct (baseReport, metric, identifier) { | ||
| return baseReport.complexity.halstead[metric].identifiers.indexOf(identifier) === -1; | ||
| } | ||
| function recordDistinctHalsteadMetric (baseReport, metric, identifier) { | ||
| baseReport.complexity.halstead[metric].identifiers.push(identifier); | ||
| } | ||
| function incrementHalsteadMetric (baseReport, metric, type) { | ||
| if (baseReport) { | ||
| baseReport.complexity.halstead[metric][type] += 1; | ||
| } | ||
| } | ||
| function processChildrenInNewScope (node, assignedName) { | ||
| var newReport = createFunctionReport(safeName(node.id, assignedName), node.loc); | ||
| function incrementTotalHalsteadItems (baseReport, metric) { | ||
| incrementHalsteadMetric(baseReport, metric, 'total'); | ||
| } | ||
| report.functions.push(newReport); | ||
| function processChildrenInNewScope (node, assignedName) { | ||
| var newReport = createFunctionReport(safeName(node.id, assignedName), node.loc); | ||
| processChildren(node, newReport, {}, {}); | ||
| } | ||
| report.functions.push(newReport); | ||
| function processChildren (node, currentReport, currentOperators, currentOperands) { | ||
| var def = syntaxDefinitions[node.type], i; | ||
| processChildren(node, newReport); | ||
| } | ||
| if (check.isArray(def.children)) { | ||
| for (i = 0; i < def.children.length; i += 1) { | ||
| processChild( | ||
| node[def.children[i]], | ||
| check.isFunction(def.getAssignedName) ? def.getAssignedName(node) : '', | ||
| currentReport, | ||
| currentOperators, | ||
| currentOperands | ||
| ); | ||
| } | ||
| function processChildren (node, currentReport) { | ||
| var def = syntaxDefinitions[node.type], i; | ||
| if (check.isArray(def.children)) { | ||
| for (i = 0; i < def.children.length; i += 1) { | ||
| processChild( | ||
| node[def.children[i]], | ||
| check.isFunction(def.getAssignedName) ? def.getAssignedName(node) : '', | ||
| currentReport | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| function processChild (child, assignedName, currentReport, currentOperators, currentOperands) { | ||
| var fn = check.isArray(child) ? processTree : processNode; | ||
| fn(child, assignedName, currentReport, currentOperators, currentOperands); | ||
| function processChild (child, assignedName, currentReport) { | ||
| var fn = check.isArray(child) ? processTree : processNode; | ||
| fn(child, assignedName, currentReport); | ||
| } | ||
| function calculateHalsteadMetrics () { | ||
| var i; | ||
| for (i = 0; i < report.functions.length; i += 1) { | ||
| calculateHalsteadFunctionMetrics(report.functions[i].complexity.halstead); | ||
| } | ||
| }()); | ||
| calculateHalsteadFunctionMetrics(report.aggregate.complexity.halstead); | ||
| } | ||
| function calculateHalsteadFunctionMetrics (fn) { | ||
| fn.length = fn.operators.total + fn.operands.total; | ||
| fn.vocabulary = fn.operators.distinct + fn.operands.distinct; | ||
| fn.difficulty = (fn.operators.distinct / 2) * (fn.operands.total / fn.operands.distinct); | ||
| fn.volume = fn.length * (Math.log(fn.vocabulary) / Math.log(2)); | ||
| fn.effort = fn.difficulty * fn.volume; | ||
| fn.bugs = fn.volume / 3000; | ||
| fn.time = fn.effort / 18; | ||
| } | ||
@@ -40,6 +40,12 @@ /*globals exports */ | ||
| ' Function: ', report.name, '\n', | ||
| ' Lines: ', report.lines.start.line, '-', report.lines.end.line, '\n', | ||
| ' Cyclomatic complexity: ', report.complexity.cyclomatic | ||
| ' Line No.: ', report.lines.start.line, '\n', | ||
| ' Length: ', report.lines.end.line - report.lines.start.line + 1, ' lines\n', | ||
| ' Cyclomatic complexity: ', report.complexity.cyclomatic, '\n', | ||
| ' Halstead length: ', report.complexity.halstead.length, '\n', | ||
| ' Halstead vocabulary: ', report.complexity.halstead.vocabulary, '\n', | ||
| ' Halstead difficulty: ', report.complexity.halstead.difficulty, '\n', | ||
| ' Halstead volume: ', report.complexity.halstead.volume, '\n', | ||
| ' Halstead effort: ', report.complexity.halstead.effort | ||
| ].join(''); | ||
| } | ||
Sorry, the diff of this file is too big to display
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
108179
14.75%2135
13.93%