complexity-report
Advanced tools
Comparing version 0.2.8 to 0.3.0
{ | ||
"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 @@ }, |
@@ -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
108179
2135