escomplex
!Build statusci-imageci-status
!Dependenciesdep-imagedep-status
!Dev Dependenciesdevdep-imagedevdep-statusSoftware complexity analysis of JavaScript-family abstract syntax trees. The back-end for complexity-report.
* [Arguments](#arguments)
* [ast](#ast)
* [walker](#walker)
* [options](#options)
* [Result](#result)
* [For a single module](#for-a-single-module)
* [For multiple modules](#for-multiple-modules)
Metrics
Currently the library reports on:- Lines of code:
- Number of parameters:
arguments
object.
Lower is better.- Cyclomatic complexity:
- Cyclomatic complexity density:
- Halstead metrics:
- Maintainability index:
- Dependencies:
require
.
Analysed statically
from the function signature,
so no accounting is made
for dynamic calls
where a variable or function is
obscuring the nature of the dependency.
Lower is better.- First-order density:
- Change cost:
- Core size:
It is important to note that none of these metrics can compete with the insight of a competent developer. At best, they are an automatable warning system, which can help to identify areas of code that warrant closer inspection by a human being.
Links to research
by Thomas J McCabe. by Geoffrey K. Gill and Chris F. Kemerer. by Horst Zuse.- Exploring the Structure of Complex Software Designs: An Empirical Study of Open Source and Proprietary Codedsm,
- The Impact of Software Design Structure on Product Maintenance Costs and Measurement of Economic Benefits of Product Redesignakaikine,
Installation
The library is published on npm under the nameescomplex
.
To install,
you can add it to the dependencies
in your package.json
file
or simply run:npm i escomplex --save
Usage
You can load escomplex in your own code by callingrequire
:const escomplex = require('escomplex');
escomplex exports two primary functions,
analyze
and processResults
analyse
const result = escomplex.analyse(source, options);
Arguments
ast
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.options
The third argument,options
,
is an optional object
containing properties that modify
some of the complexity calculations:options.logicalor
:
||
should be considered a source of cyclomatic complexity,
defaults to true
.options.switchcase
:
switch
statements
should be considered a source of cyclomatic complexity,
defaults to true
.options.forin
:
for
...in
loops
should be considered a source of cyclomatic complexity,
defaults to false
.options.trycatch
:
catch
clauses
should be considered a source of cyclomatic complexity,
defaults to false
.options.newmi
:
false
.options.skipCalculation
:
options.noCoreSize
:
processResults
const noCoreSize = false;
escomplex.processResults(result, noCoreSize);
This function takes a report object and computes aggregate scores for all individual files and also adjacency and visibility matrices. This is useful for combining together multiple report objects and recomputing aggregate scores.
Arguments
Result
A result object of the form:var result = {
reports: [
{
// same format as module return
}
]
}
noCoreSize
a boolean indicating not to calculate the visibilityMatrix or core sizeResult Format
Bothanalyze
and processResults
return a report of the following format,
with some variation depending on the given options.For a single module
If a single abstract syntax tree object is passed in theast
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
:
report.dependencies
:
report.aggregate.sloc.physical
:
undefined
if the syntax tree
is not annotated
with line number data.report.aggregate.sloc.logical
:
report.aggregate.params
:
report.aggregate.cyclomatic
:
report.aggregate.cyclomaticDensity
:
report.aggregate.halstead.vocabulary
:
report.aggregate.halstead.difficulty
:
report.aggregate.halstead.volume
:
report.aggregate.halstead.effort
:
report.aggregate.halstead.bugs
:
report.aggregate.halstead.time
:
report.functions[n].name
:
report.functions[n].line
:
undefined
if the syntax tree
is not annotated
with line number data.report.functions[n].sloc.physical
:
undefined
if the syntax tree
is not annotated
with line number data.report.functions[n].sloc.logical
:
report.functions[n].params
:
report.functions[n].cyclomatic
:
report.functions[n].cyclomaticDensity
:
report.functions[n].halstead.vocabulary
:
report.functions[n].halstead.difficulty
:
report.functions[n].halstead.volume
:
report.functions[n].halstead.effort
:
report.functions[n].halstead.bugs
:
report.functions[n].halstead.time
:
For multiple modules
If an array of sources is passed in thesource
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
:
path
that matches the path
property
from its corresponding syntax tree.
This path
property is required
because the reports array gets sorted
during dependency analysis.result.adjacencyMatrix
:
reports
array.
Each row and column
represents its equivalent
indexed module
from the reports
array,
with values along the horizontal
being 1
when that module
directly depends on another
and values along the vertical
being 1
when that module
is directly depended on by another.
All other values are 0
.result.firstOrderDensity
:
result.visibilityMatrix
:
noCoreSize
is passed
as an option.result.changeCost
:
noCoreSize
is passed
as an option.result.coreSize
:
result.loc
:
result.cyclomatic
:
result.effort
:
result.params
:
result.maintainability
:
Related projects
JavaScript source code visualization, static analysis, and complexity tool.- jsc:
- bob:
Development
Refer to the contribution guidelinescontributions 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.