Software complexity analysis of JavaScript abstract syntax trees. The back-end for complexity-report.
The library is published on npm
under the name escomplex
.
To install,
you can add it to the dependencies
in your package.json
file
or simply run:
npm i escomplex --save
You can use escomplex by including it as an Node.js module:
const escomplex = require('escomplex');
The module exports the analyse
function.
The analyse
function is used to convert the source code of one or more modules
into one or more corresponding report objects containing metrics.
const result = escomplex.analyse(source, options);
The first argument, source
, must be either a string or an array of objects. If it is an array, each object should include a path
property that is either a relative or full path to the equivalent module on disk and a code
with the contents of the module. As well as identifying each of the result objects, the path property is also used during dependency analysis.
The third argument, options
,
is an optional object
containing properties that modify
some of the complexity calculations:
options.logicalor
: Boolean indicating whether operator||
should be considered a source of cyclomatic complexity, defaults totrue
.options.switchcase
: Boolean indicating whetherswitch
statements should be considered a source of cyclomatic complexity, defaults totrue
.options.forin
: Boolean indicating whetherfor
...in
loops should be considered a source of cyclomatic complexity, defaults tofalse
.options.trycatch
: Boolean indicating whethercatch
clauses should be considered a source of cyclomatic complexity, defaults tofalse
.options.newmi
: Boolean indicating whether the maintainability index should be rebased on a scale from 0 to 100, defaults tofalse
.options.skipCalculation
: only valid for when source is an array of files Boolean indicating if we should skip processing of certain values, such as the adjacency and visibility matrixes, core sizes, and average values loc, etc.options.noCoreSize
: Skips creating the visibility matrix and calculating the coreSize, which can be very expensive for large projects
The analyze
function returns
a report of the following format,
with some variation depending on the given options.
If a single source string
is passed in the source
argument,
the result will be a report object
that looks like the following:
{
maintainability: 171,
dependencies: [],
aggregate: {
sloc: {
logical: 0,
physical: 0
},
params: 0,
cyclomatic: 1,
cyclomaticDensity: 1,
halstead: {
vocabulary: 0,
difficulty: 0,
volume: 0,
effort: 0,
bugs: 0,
time: 0
}
},
functions: [
{
name: '',
line: 0,
sloc: {
logical: 0,
physical: 0
},
params: 0,
cyclomatic: 1,
cyclomaticDensity: 1,
halstead: {
vocabulary: 0,
difficulty: 0,
volume: 0,
effort: 0,
bugs: 0,
time: 0
}
},
...
]
}
The meaning of those values, briefly, is as follows (see metrics for more information on each one):
report.maintainability
: The maintainability index for the module.report.dependencies
: The array of CommonJS/AMD dependencies for the module.report.aggregate.sloc.physical
: Physical lines of code for the module. Will beundefined
if the syntax tree is not annotated with line number data.report.aggregate.sloc.logical
: Logical lines of code for the module.report.aggregate.params
: Parameter count for the module.report.aggregate.cyclomatic
: Cyclomatic complexity for the module.report.aggregate.cyclomaticDensity
: Cyclomatic complexity density for the module.report.aggregate.halstead.vocabulary
: Halstead vocabulary size for the module.report.aggregate.halstead.difficulty
: Halstead difficulty for the module.report.aggregate.halstead.volume
: Halstead volume for the module.report.aggregate.halstead.effort
: Halstead effort for the module.report.aggregate.halstead.bugs
: Halstead bugs for the module.report.aggregate.halstead.time
: Halstead time for the module.report.functions[n].name
: Function name.report.functions[n].line
: Line number that the function starts on. Will beundefined
if the syntax tree is not annotated with line number data.report.functions[n].sloc.physical
: Physical lines of code for the function. Will beundefined
if the syntax tree is not annotated with line number data.report.functions[n].sloc.logical
: Logical lines of code for the function.report.functions[n].params
: Parameter count for the function.report.functions[n].cyclomatic
: Cyclomatic complexity for the function.report.functions[n].cyclomaticDensity
: Cyclomatic complexity density for the function.report.functions[n].halstead.vocabulary
: Halstead vocabulary size for the function.report.functions[n].halstead.difficulty
: Halstead difficulty for the function.report.functions[n].halstead.volume
: Halstead volume for the function.report.functions[n].halstead.effort
: Halstead effort for the function.report.functions[n].halstead.bugs
: Halstead bugs for the function.report.functions[n].halstead.time
: Halstead time for the function.
If an array of sources is passed in the source
argument, the result will be an object
that looks like the following:
{
reports: [
...
],
adjacencyMatrix: [
[ 0 ]
],
firstOrderDensity: 0,
visibilityMatrix: [
[ 0 ]
],
changeCost: 100,
coreSize: 100,
loc: 0,
cyclomatic: 1,
effort: 0,
params: 0,
maintainability: 171
}
Those properties are defined as follows:
result.reports
: An array of report objects, each one in the same format described above but with an extra propertypath
that matches thepath
property from its corresponding syntax tree. Thispath
property is required because the reports array gets sorted during dependency analysis.result.adjacencyMatrix
: The adjacency design structure matrix (DSM) for the project. This is a two-dimensional array, each dimension with the same order and length as thereports
array. Each row and column represents its equivalent indexed module from thereports
array, with values along the horizontal being1
when that module directly depends on another and values along the vertical being1
when that module is directly depended on by another. All other values are0
.result.firstOrderDensity
: The first-order density for the project.result.visibilityMatrix
: The visibility DSM for the project. Like the adjacency matrix, but expanded to incorporate indirect dependencies. Will be missing ifnoCoreSize
is passed as an option.result.changeCost
: The change cost for the project. Will be missing ifnoCoreSize
is passed as an option.result.coreSize
: The core size for the project.result.loc
: The average per-function count of logical lines of code.result.cyclomatic
: The average per-function cyclomatic complexity.result.effort
: The average per-function Halstead effort.result.params
: The average per-function parameter count.result.maintainability
: The average per-module maintainability index.
Refer to a more in-depth description of the metrics used for more details.
- plato: JavaScript source code visualization, static analysis, and complexity tool.
- jsc: JavaScript source code complexity tool.
- bob: Minimalist-omakase build tool for node.js projects.
- cardio: A web application health tool.
- grunt-complexity: A JavaScript complexity analysis grunt task.
- atom-linter-escomplex: Lining code complexity in Atom editor.
- brackets-crjs: Brackets extension.
- jscomplexity: JS cyclomatic complexity report generator.
- karma-complexity-processor: A preprocessor for karma runner to give some metrics about code complexity.
- crlint: JS linter based on complexity report results.
All changes should be submitted in the form of a pull request. Please refer to the contribution guidelines before submitting a pull request.
Source code is in /src
.
Unit tests are in /test
.
You can run the tests with npm test
.
You can run the linter with npm run lint
.
Make sure you've installed
all the dependencies
with npm install
first.