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.4.3 to 0.5.0

src/formats/minimal.js

4

config/jshint.json

@@ -27,3 +27,3 @@ {

"funcscope": false,
"globalstrict": false,
"globalstrict": true,
"iterator": false,

@@ -44,3 +44,3 @@ "lastsemic": false,

"validthis": false,
"browser": false,

@@ -47,0 +47,0 @@ "couch": false,

@@ -10,4 +10,4 @@ /*jshint nomen:false */

commands = {
test: './node_modules/.bin/mocha --ui tdd --reporter spec --colors --slow 50 ./test/complexityReport.js',
lint: './node_modules/.bin/jshint ./src/complexityReport.js --config config/jshint.json',
test: './node_modules/.bin/mocha --ui tdd --reporter spec --colors ./test/complexityReport.js',
lint: './node_modules/.bin/jshint ./src --config config/jshint.json',
prepare: 'npm install'

@@ -17,3 +17,3 @@ };

desc('Run the unit tests.');
task('test', [ 'prepare' ], function () {
task('test', function () {
runTask(test, 'Testing...');

@@ -25,3 +25,3 @@ }, {

desc('Lint the source code.');
task('lint', [ 'prepare' ], function () {
task('lint', function () {
runTask(lint, 'Linting...');

@@ -28,0 +28,0 @@ }, {

{
"name": "complexity-report",
"version": "0.4.3",
"version": "0.5.0",
"author": "Phil Booth <pmbooth@gmail.com>",

@@ -5,0 +5,0 @@ "description": "A tool for reporting code complexity metrics in JavaScript projects.",

@@ -16,4 +16,5 @@ # complexityReport.js

to aid its usefulness in automated environments / CI.
There are also some options
for controlling how metrics are calculated.
There are also options
for controlling how metrics are calculated
and the format of the report output.

@@ -23,2 +24,8 @@ The metrics are calculated by walking syntax trees

For people who are only interested in analysing small amounts of code
and don't want to download the tool,
there is also a web front-end available:
* [JSComplexity.org][jscomplexity]
## Installation

@@ -97,3 +104,3 @@

defaults to `true`.
* `switchcase`: Boolean indicating whether `switch` statements
* `switchcase`: Boolean indicating whether `switch` statements
should be considered a source of cyclomatic complexity,

@@ -116,2 +123,10 @@ defaults to `true`.

## Related projects
* [Gleb Bahmutov][gleb]'s [js-complexity-viz]
is a wrapper for complexityReport.js,
which uses the Google Visualization API
to render complexity charts
for all JavaScript files in a directory tree.
## Development

@@ -161,5 +176,5 @@

### Unit tests
### Tests
The unit tests are in `test/complexityReport.js`. You can run them with the
The tests are in `test/complexityReport.js`. You can run them with the
command `npm test` or `jake test`.

@@ -170,2 +185,5 @@

[esprima]: http://esprima.org/
[jscomplexity]: http://jscomplexity.org/
[gleb]: https://github.com/bahmutov
[js-complexity-viz]: https://github.com/bahmutov/js-complexity-viz
[tracker]: https://github.com/philbooth/complexityReport.js/issues

@@ -172,0 +190,0 @@ [node]: http://nodejs.org/

#!/usr/bin/env node
/*globals require, process */
/*globals require, process, console */
(function () {
'use strict';
'use strict';
var reports = [],
var reports = [],
cli = require('commander'),
fs = require('fs'),
cr = require('./complexityReport'),
check = require('check-types'),
options,
formatter,
cli = require('commander'),
fs = require('fs'),
cr = require('./complexityReport'),
check = require('check-types'),
options,
formatter,
state = {
reading: true,
unread: 0,
tooComplex: false
};
state = {
reading: true,
unread: 0,
tooComplex: false
};
parseCommandLine();
readSourceFiles();
parseCommandLine();
readSourceFiles();
function parseCommandLine () {
cli.
usage('[options] <file...>').
option(
'-o, --output <file>',
'specify an output file for the report'
).
option(
'-f, --format <format>',
'specify the output format of the report'
).
option(
'-t, --threshold <complexity>',
'specifify the per-function complexity threshold',
function (value) {
return parseInt(value, 10);
}
).
option(
'-l, --logicalor',
'disregard operator || as source of cyclomatic complexity'
).
option(
'-s, --switchcase',
'disregard switch statements as source of cyclomatic complexity'
).
option(
'-i, --forin',
'treat for...in statements as source of cyclomatic complexity'
).
option(
'-c, --trycatch',
'treat catch clauses as source of cyclomatic complexity'
);
function parseCommandLine () {
cli.
usage('[options] <file...>').
option(
'-o, --output <file>',
'specify an output file for the report'
).
option(
'-f, --format <format>',
'specify the output format of the report'
).
option(
'-t, --threshold <complexity>',
'specifify the per-function complexity threshold',
function (value) {
return parseInt(value, 10);
}
).
option(
'-s, --silent',
'don\'t write any output to the console'
).
option(
'-l, --logicalor',
'disregard operator || as source of cyclomatic complexity'
).
option(
'-w, --switchcase',
'disregard switch statements as source of cyclomatic complexity'
).
option(
'-i, --forin',
'treat for...in statements as source of cyclomatic complexity'
).
option(
'-c, --trycatch',
'treat catch clauses as source of cyclomatic complexity'
);
cli.parse(process.argv);
cli.parse(process.argv);
options = {
logicalor: !cli.logicalor,
switchcase: !cli.switchcase,
forin: cli.forin || false,
trycatch: cli.trycatch || false
};
options = {
logicalor: !cli.logicalor,
switchcase: !cli.switchcase,
forin: cli.forin || false,
trycatch: cli.trycatch || false
};
if (check.isUnemptyString(cli.format) === false) {
cli.format = 'plain';
}
formatter = require('./formats/' + cli.format);
if (check.isUnemptyString(cli.format) === false) {
cli.format = 'plain';
}
formatter = require('./formats/' + cli.format);
}
function readSourceFiles () {
var i;
function readSourceFiles () {
var i;
for (i = 0; i < cli.args.length; i += 1) {
state.unread += 1;
readSourceFile(cli.args[i]);
}
state.reading = false;
for (i = 0; i < cli.args.length; i += 1) {
state.unread += 1;
readSourceFile(cli.args[i]);
}
function readSourceFile (path) {
fs.readFile(path, 'utf8', function (err, source) {
if (err) {
error('readSourceFile', err);
}
state.reading = false;
}
if (beginsWithShebang(source)) {
source = commentFirstLine(source);
}
function readSourceFile (path) {
fs.readFile(path, 'utf8', function (err, source) {
if (err) {
error('readSourceFile', err);
}
getReport(path, source);
if (beginsWithShebang(source)) {
source = commentFirstLine(source);
}
finish();
});
}
getReport(path, source);
function error (functionName, err) {
fail('Fatal error [' + functionName + ']: ' + err.message);
}
finish();
});
}
function fail (message) {
console.log(message);
process.exit(1);
}
function error (functionName, err) {
fail('Fatal error [' + functionName + ']: ' + err.message);
}
function beginsWithShebang (source) {
return source[0] === '#' && source[1] === '!';
}
function fail (message) {
console.log(message);
process.exit(1);
}
function commentFirstLine (source) {
return '//' + source;
}
function beginsWithShebang (source) {
return source[0] === '#' && source[1] === '!';
}
function getReport (path, source) {
var report = cr.run(source, options);
function commentFirstLine (source) {
return '//' + source;
}
if (
state.tooComplex === false &&
check.isNumber(cli.threshold) &&
isTooComplex(report)
) {
state.tooComplex = true;
}
function getReport (path, source) {
var report = cr.run(source, options);
report.module = path;
reports.push(report);
if (
state.tooComplex === false &&
check.isNumber(cli.threshold) &&
isTooComplex(report)
) {
state.tooComplex = true;
}
function isTooComplex (report) {
var i;
report.module = path;
for (i = 0; i < report.functions.length; i += 1) {
if (report.functions[i].complexity.cyclomatic > cli.threshold) {
return true;
}
}
reports.push(report);
}
return false;
}
function isTooComplex (report) {
var i;
function finish () {
state.unread -= 1;
if (state.reading === false && state.unread === 0) {
writeReport();
for (i = 0; i < report.functions.length; i += 1) {
if (report.functions[i].complexity.cyclomatic > cli.threshold) {
return true;
}
}
function writeReport () {
var formatted = formatter.format(reports);
return false;
}
if (check.isUnemptyString(cli.output)) {
fs.writeFile(cli.output, formatted, 'utf8', function (err) {
if (err) {
error('writeReport', err);
}
});
} else {
console.log(formatted);
function finish () {
state.unread -= 1;
if (state.reading === false && state.unread === 0) {
if (!cli.silent) {
writeReport();
}
exit();
}
function exit () {
// TODO: Use Q
if (state.tooComplex) {

@@ -179,3 +163,17 @@ fail('Warning: Complexity threshold breached!');

}
}());
}
function writeReport () {
var formatted = formatter.format(reports);
if (check.isUnemptyString(cli.output)) {
fs.writeFile(cli.output, formatted, 'utf8', function (err) {
if (err) {
error('writeReport', err);
}
});
} else {
console.log(formatted);
}
}

@@ -5,3 +5,2 @@ /**

/*jshint globalstrict:true */
/*globals exports, require */

@@ -11,3 +10,3 @@

var check, esprima, syntaxDefinitions, report;
var check, esprima, syntaxDefinitions, safeName, syntaxes, report;

@@ -18,2 +17,4 @@ exports.run = run;

esprima = require('esprima');
safeName = require('./safeName');
syntaxDefinitions = require('./syntax');

@@ -31,2 +32,4 @@ /**

function run (source, options) {
// TODO: Asynchronize.
var settings, ast;

@@ -46,3 +49,3 @@

syntaxDefinitions = getSyntaxDefinitions(settings);
syntaxes = syntaxDefinitions.get(settings);
report = createReport(ast.loc);

@@ -66,232 +69,2 @@

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: {
operators: [ { identifier: '{}' } ],
operands: [ { identifier: safeName } ],
children: [ 'properties' ]
},
Property: {
lloc: 1,
operators: [ { identifier: ':' } ],
children: [ 'key', 'value' ],
getAssignedName: function (node) { return safeName(node.key); }
},
ThisExpression: {
operands: [ { identifier: 'this' } ]
},
ArrayExpression: {
operators: [ { identifier: '[]' } ],
operands: [ { identifier: safeName } ],
children: [ 'elements' ]
},
MemberExpression: {
lloc: function (node) {
return [
'ObjectExpression',
'ArrayExpression',
'FunctionExpression'
].indexOf(node.object.type) === -1 ? 0 : 1;
},
operators: [ { identifier: '.' } ],
children: [ 'object', 'property' ]
},
CallExpression: getFunctionCallSyntaxDefinition('()'),
NewExpression: getFunctionCallSyntaxDefinition('new'),
ExpressionStatement: {
lloc: 1,
children: [ 'expression' ]
},
VariableDeclaration: {
operators: [ { identifier: function (node) { return node.kind; } } ],
children: [ 'declarations' ]
},
VariableDeclarator: {
lloc: 1,
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);
}
},
UnaryExpression: {
operators: [ {
identifier: function (node) {
return node.operator + ' (' + (node.prefix ? 'pre' : 'post') + 'fix)';
}
} ],
children: [ 'argument' ]
},
UpdateExpression: {
operators: [ {
identifier: function (node) {
return node.operator + ' (' + (node.prefix ? 'pre' : 'post') + 'fix)';
}
} ],
children: [ 'argument' ]
},
BinaryExpression: {
operators: [ { identifier: function (node) { return node.operator; } } ],
children: [ 'left', 'right' ]
},
LogicalExpression: {
complexity: function (node) { return settings.logicalor && node.operator === '||' ? 1 : 0; },
operators: [ { identifier: function (node) { return node.operator; } } ],
children: [ 'left', 'right' ]
},
SequenceExpression: {
children: [ 'expressions' ]
},
IfStatement: {
lloc: function (node) { return node.alternate ? 2 : 1; },
complexity: 1,
operators: [ {
identifier: 'if'
}, {
identifier: 'else',
optional: function (node) { return !!node.alternate; }
} ],
children: [ 'test', 'consequent', 'alternate' ]
},
ConditionalExpression: {
complexity: 1,
operators: [ { identifier: ':?' } ],
children: [ 'test', 'consequent', 'alternate' ]
},
SwitchStatement: {
lloc: 1,
operators: [ { identifier: 'switch' } ],
children: [ 'discriminant', 'cases' ]
},
SwitchCase: {
lloc: 1,
complexity: function (node) { return settings.switchcase && node.test ? 1 : 0; },
operators: [ {
identifier: function (node) {
return node.test ? 'case' : 'default';
}
} ],
children: [ 'test', 'consequent' ]
},
BreakStatement: getBreakContinueSyntaxDefinition('break'),
ContinueStatement: getBreakContinueSyntaxDefinition('continue'),
ForStatement: getLoopSyntaxDefinition('for', 1),
ForInStatement: {
lloc: 1,
complexity: function (node) { return settings.forin ? 1 : 0; },
operators: [ { identifier: 'forin' } ],
children: [ 'left', 'right', 'body' ]
},
WhileStatement: getLoopSyntaxDefinition('while', 1),
DoWhileStatement: getLoopSyntaxDefinition('dowhile', 2),
FunctionDeclaration: getFunctionSyntaxDefinition(1),
FunctionExpression: getFunctionSyntaxDefinition(0),
ReturnStatement: {
lloc: 1,
operators: [ { identifier: 'return' } ],
children: [ 'argument' ]
},
TryStatement: {
lloc: 1,
children: [ 'block', 'handlers' ]
},
CatchClause: {
lloc: 1,
complexity: function (node) { return settings.trycatch ? 1 : 0; },
operators: [ { identifier: 'catch' } ],
children: [ 'param', 'body' ]
},
ThrowStatement: {
lloc: 1,
operators: [ { identifier: 'throw' } ],
children: [ 'argument' ]
},
WithStatement: {
lloc: 1,
operators: [ { identifier: 'with' } ],
children: [ 'object', 'body' ]
}
};
}
function safeName (object, defaultName) {
if (check.isObject(object) && check.isUnemptyString(object.name)) {
return object.name;
}
if (check.isUnemptyString(defaultName)) {
return defaultName;
}
return '<anonymous>';
}
function getFunctionCallSyntaxDefinition (type) {
return {
lloc: function (node) { return node.callee.type === 'FunctionExpression' ? 1 : 0; },
operators: [ { identifier: type } ],
children: [ 'arguments', 'callee' ]
};
}
function getBreakContinueSyntaxDefinition (type) {
return {
lloc: 1,
operators: [ { identifier: true } ],
children: [ 'label' ]
};
}
function getLoopSyntaxDefinition (type, lloc) {
return {
lloc: lloc,
complexity: function (node) { return node.test ? 1 : 0; },
operators: [ { identifier: type } ],
children: [ 'init', 'test', 'update', 'body' ]
};
}
function getFunctionSyntaxDefinition (lloc) {
return {
lloc: lloc,
operators: [ { identifier: 'function' } ],
operands: [ { identifier: function (node) { return safeName(node.id); } } ],
children: [ 'params', 'body' ],
isFunction: true
};
}
function createReport (lines) {

@@ -345,8 +118,8 @@ return {

function processNode (node, assignedName, currentReport) {
var def;
var syntax;
if (check.isObject(node)) {
def = syntaxDefinitions[node.type];
if (check.isObject(syntaxDefinitions[node.type])) {
syntax = syntaxes[node.type];
if (check.isObject(syntaxes[node.type])) {
processLloc(node, currentReport);

@@ -356,4 +129,4 @@ processComplexity(node, currentReport);

processOperands(node, currentReport);
if (def.isFunction) {
if (syntax.newScope) {
processChildrenInNewScope(node, assignedName);

@@ -372,3 +145,3 @@ } else {

function incrementCounter (node, name, incrementFn, currentReport) {
var amount = syntaxDefinitions[node.type][name];
var amount = syntaxes[node.type][name];

@@ -411,15 +184,15 @@ if (check.isNumber(amount)) {

function processHalsteadMetric (node, metric, currentReport) {
var def = syntaxDefinitions[node.type], i, identifier;
var syntax = syntaxes[node.type], i, identifier;
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);
if (check.isArray(syntax[metric])) {
for (i = 0; i < syntax[metric].length; i += 1) {
if (check.isFunction(syntax[metric][i].identifier)) {
identifier = syntax[metric][i].identifier(node);
} else {
identifier = def[metric][i].identifier;
identifier = syntax[metric][i].identifier;
}
if (
check.isFunction(def[metric][i].optional) === false ||
def[metric][i].optional(node) === true
check.isFunction(syntax[metric][i].filter) === false ||
syntax[metric][i].filter(node) === true
) {

@@ -482,9 +255,9 @@ halsteadItemEncountered(currentReport, metric, identifier);

function processChildren (node, currentReport) {
var def = syntaxDefinitions[node.type], i;
var syntax = syntaxes[node.type], i;
if (check.isArray(def.children)) {
for (i = 0; i < def.children.length; i += 1) {
if (check.isArray(syntax.children)) {
for (i = 0; i < syntax.children.length; i += 1) {
processChild(
node[def.children[i]],
check.isFunction(def.getAssignedName) ? def.getAssignedName(node) : '',
node[syntax.children[i]],
check.isFunction(syntax.assignableName) ? syntax.assignableName(node) : '',
currentReport

@@ -503,5 +276,5 @@ );

var i, data, averages,
sums = [ 0, 0, 0 ],
indices = {

@@ -515,3 +288,3 @@ loc: 0,

data = report.functions[i].complexity;
calculateHalsteadMetrics(data.halstead);

@@ -518,0 +291,0 @@ sumMaintainabilityMetrics(sums, indices, data);

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