New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

complexity-report

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

complexity-report - npm Package Compare versions

Comparing version 0.2.8 to 0.3.0

4

package.json
{
"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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc