Socket
Socket
Sign inDemoInstall

escomplex-plugin-metrics-project

Package Overview
Dependencies
1
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.10 to 0.0.11

dist/ProjectMetricCalculate.js

370

dist/PluginMetricsProject.js
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
value: true
});

@@ -9,10 +9,6 @@

var _MathUtil = require('typhonjs-escomplex-commons/dist/utils/MathUtil');
var _ProjectMetricCalculate = require('./ProjectMetricCalculate');
var _MathUtil2 = _interopRequireDefault(_MathUtil);
var _ProjectMetricCalculate2 = _interopRequireDefault(_ProjectMetricCalculate);
var _ObjectUtil = require('typhonjs-escomplex-commons/dist/utils/ObjectUtil');
var _ObjectUtil2 = _interopRequireDefault(_ObjectUtil);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -30,334 +26,42 @@

var PluginMetricsProject = function () {
function PluginMetricsProject() {
_classCallCheck(this, PluginMetricsProject);
}
function PluginMetricsProject() {
_classCallCheck(this, PluginMetricsProject);
}
_createClass(PluginMetricsProject, [{
key: 'onConfigure',
_createClass(PluginMetricsProject, [{
key: 'onConfigure',
// ESComplexProject plugin callbacks -----------------------------------------------------------------------------
/**
* Loads any default settings that are not already provided by any user options.
*
* @param {object} ev - escomplex plugin event data.
*
* The following options are:
* ```
* (boolean) newmi - Boolean indicating whether the maintainability index should be rebased on a scale from
* 0 to 100; defaults to false.
* ```
*/
value: function onConfigure(ev) {
ev.data.settings.noCoreSize = typeof ev.data.options.noCoreSize === 'boolean' ? ev.data.options.noCoreSize : false;
}
/**
* Loads any default settings that are not already provided by any user options.
*
* @param {object} ev - escomplex plugin event data.
*
* The following options are:
* ```
* (boolean) newmi - Boolean indicating whether the maintainability index should be rebased on a scale from
* 0 to 100; defaults to false.
* ```
*/
value: function onConfigure(ev) {
ev.data.settings.noCoreSize = typeof ev.data.options.noCoreSize === 'boolean' ? ev.data.options.noCoreSize : false;
}
/**
* Performs final calculations based on collected project report data.
*
* @param {object} ev - escomplex plugin event data.
*/
/**
* Performs final calculations based on collected project report data.
*
* @param {object} ev - escomplex plugin event data.
*/
}, {
key: 'onProjectEnd',
value: function onProjectEnd(ev) {
var pathModule = ev.data.pathModule;
var projectReport = ev.data.projectReport;
var settings = ev.data.settings;
}, {
key: 'onProjectEnd',
value: function onProjectEnd(ev) {
var pathModule = ev.data.pathModule;
var projectReport = ev.data.projectReport;
_ProjectMetricCalculate2.default.calculate(pathModule, projectReport, settings);
}
}]);
var adjacencyMatrix = this._calculateAdjacencyMatrix(pathModule, projectReport);
if (!this.settings.noCoreSize) {
var visibilityMatrix = this._calculateVisibilityMatrix(projectReport, adjacencyMatrix);
this._calculateCoreSize(projectReport, visibilityMatrix);
}
this._calculateAverages(projectReport);
}
/**
* Stores settings.
*
* @param {object} ev - escomplex plugin event data.
*/
}, {
key: 'onProjectStart',
value: function onProjectStart(ev) {
/**
* Stores the settings for all ESComplexProject plugins.
* @type {object}
*/
this.settings = ev.data.settings;
}
// Project metrics calculation -----------------------------------------------------------------------------------
/**
* Calculates an adjacency matrix for all modules based on ES Module and CommonJS dependencies also storing a
* compacted while returning the matrix for further calculation. Each row index corresponds to the same module index.
* Each row entry corresponds to a module index. These relationships dictate the dependencies between all
* module ModuleReports given the source paths.
*
* @param {object} pathModule - A module that conforms to the Node path API.
* @param {object} projectReport - The ProjectResult being processed.
*
* @returns {Array<Array<number>>}
* @private
*/
}, {
key: '_calculateAdjacencyMatrix',
value: function _calculateAdjacencyMatrix(pathModule, projectReport) {
var modules = projectReport.modules;
var length = modules.length;
var adjacencyMatrix = _MathUtil2.default.create2DArray(length, 0);
var density = 0;
for (var x = 0; x < length; x++) {
for (var y = 0; y < length; y++) {
adjacencyMatrix[x][y] = x !== y && this._doesDependencyExist(pathModule, modules[x], modules[y]) ? 1 : 0;
if (adjacencyMatrix[x][y] === 1) {
density += 1;
}
}
}
projectReport.adjacencyList = _MathUtil2.default.compactMatrix(adjacencyMatrix);
projectReport.firstOrderDensity = _MathUtil2.default.getPercent(density, length * length);
return adjacencyMatrix;
}
/**
* Calculates average ModuleReport metrics that are applicable to ProjectResult.
*
* @param {object} projectReport - The ProjectResult being processed.
*
* @private
*/
}, {
key: '_calculateAverages',
value: function _calculateAverages(projectReport) {
var divisor = projectReport.modules.length === 0 ? 1 : projectReport.modules.length;
var moduleAverage = projectReport.moduleAverage;
var moduleAverageKeys = _ObjectUtil2.default.getAccessorList(moduleAverage);
// Defer to ModuleReport to sum all relevant module metrics applicable to ProjectResult.
projectReport.modules.forEach(function (module) {
moduleAverageKeys.forEach(function (averageKey) {
var targetValue = _ObjectUtil2.default.safeAccess(module, averageKey, 0);
_ObjectUtil2.default.safeSet(moduleAverage, averageKey, targetValue, 'add');
});
});
moduleAverageKeys.forEach(function (averageKey) {
_ObjectUtil2.default.safeSet(moduleAverage, averageKey, divisor, 'div');
});
}
/**
* Calculates core size which is the percentage of modules / files that are both widely depended on and themselves
* depend on other modules. Lower is better.
*
* @param {object} projectReport - The ProjectResult being processed.
* @param {Array<Array<number>>} visibilityMatrix - The calculated visibilityMatrix.
*
* @private
*/
}, {
key: '_calculateCoreSize',
value: function _calculateCoreSize(projectReport, visibilityMatrix) {
if (projectReport.firstOrderDensity === 0) {
projectReport.coreSize = 0;
return;
}
var length = visibilityMatrix.length;
var fanIn = new Array(length);
var fanOut = new Array(length);
var coreSize = 0;
var _loop = function _loop(rowIndex) {
fanIn[rowIndex] = visibilityMatrix[rowIndex].reduce(function (sum, value, valueIndex) {
fanOut[valueIndex] = rowIndex === 0 ? value : fanOut[valueIndex] + value;
return sum + value;
}, 0);
};
for (var rowIndex = 0; rowIndex < length; rowIndex++) {
_loop(rowIndex);
}
// Boundary values can also be chosen by looking for discontinuity in the
// distribution of values, but to keep it simple the median is used.
var boundaries = {
fanIn: _MathUtil2.default.getMedian(fanIn.slice()),
fanOut: _MathUtil2.default.getMedian(fanOut.slice())
};
for (var _rowIndex = 0; _rowIndex < length; _rowIndex++) {
if (fanIn[_rowIndex] >= boundaries.fanIn && fanOut[_rowIndex] >= boundaries.fanOut) {
coreSize += 1;
}
}
projectReport.coreSize = _MathUtil2.default.getPercent(coreSize, length);
}
/**
* Stores a compacted form of the visibility matrix. Each row index corresponds to the same module index.
* Each row entry corresponds to a module index. These relationships dictate the reverse visibility between all
* module ModuleReports which may indirectly impact the given module / file. The full matrix is returned for further
* calculation.
*
* Implementation of Floyd Warshall algorithm for calculating visibility matrix in O(n^3) instead of O(n^4) with
* successive raising of powers.
*
* @param {object} projectReport - The ProjectResult being processed.
* @param {Array<Array<number>>} adjacencyMatrix - The calculated adjacencyMatrix.
*
* @return {Array<Array<number>>}
* @private
*/
}, {
key: '_calculateVisibilityMatrix',
value: function _calculateVisibilityMatrix(projectReport, adjacencyMatrix) {
var changeCost = 0;
var length = adjacencyMatrix.length;
var visibilityMatrix = _MathUtil2.default.create2DArray(length, 0);
// Convert adjacency matrix to a distance matrix suitable for the Floyd Warshall algorithm.
// if i !== j and adjacency matrix value is 0 set distance to Infinity.
for (var x = 0; x < length; x++) {
for (var y = 0; y < length; y++) {
visibilityMatrix[x][y] = x === y ? 1 : adjacencyMatrix[x][y] || Infinity;
}
}
// Floyd Warshall core algorithm
for (var k = 0; k < length; k++) {
for (var _x = 0; _x < length; _x++) {
for (var _y = 0; _y < length; _y++) {
if (visibilityMatrix[_x][_y] > visibilityMatrix[_x][k] + visibilityMatrix[k][_y]) {
visibilityMatrix[_x][_y] = visibilityMatrix[_x][k] + visibilityMatrix[k][_y];
}
}
}
}
// Convert back from a distance matrix to adjacency matrix while also calculating change cost.
for (var _x2 = 0; _x2 < length; _x2++) {
for (var _y2 = 0; _y2 < length; _y2++) {
if (visibilityMatrix[_x2][_y2] < Infinity) {
changeCost++;
if (_x2 !== _y2) {
visibilityMatrix[_x2][_y2] = 1;
}
} else {
visibilityMatrix[_x2][_y2] = 0;
}
}
}
projectReport.visibilityList = _MathUtil2.default.compactMatrix(visibilityMatrix);
projectReport.changeCost = _MathUtil2.default.getPercent(changeCost, length * length);
return visibilityMatrix;
}
/**
* Determines if there is at least one dependency that matches `toModuleReport.srcPath` from all the dependencies
* stored in `fromModuleReport`.
*
* @param {object} pathModule - A module that conforms to the Node path API.
* @param {ModuleReport} fromModuleReport - A ModuleReport to match to the srcPath of `toModuleReport`.
* @param {ModuleReport} toModuleReport - A ModuleReport providing the `srcPath` to match.
*
* @returns {boolean}
* @private
*/
}, {
key: '_doesDependencyExist',
value: function _doesDependencyExist(pathModule, fromModuleReport, toModuleReport) {
var matchedDependency = false;
var fromModuleReport_dirname = pathModule.dirname(fromModuleReport.srcPath);
// First test for srcPathAlias which is the case when an NPM or JSPM module has a main entry and is mapped to a
// given name or alias.
for (var cntr = 0; cntr < fromModuleReport.dependencies.length; cntr++) {
var depPath = fromModuleReport.dependencies[cntr].path;
if (typeof toModuleReport.srcPathAlias === 'string' && depPath === toModuleReport.srcPathAlias) {
matchedDependency = true;
break;
}
}
// Exit early if alias match was found above.
if (matchedDependency) {
return true;
}
// Now test for srcPath matches.
for (var _cntr = 0; _cntr < fromModuleReport.dependencies.length; _cntr++) {
var _depPath = fromModuleReport.dependencies[_cntr].path;
// If there is no extension provided in the dependency then add the extension of the `to srcPath`.
if (pathModule.extname(_depPath) === '') {
_depPath += pathModule.extname(toModuleReport.srcPath);
}
// Best case match scenario when dependency matches toModuleReportPath.srcPath.
if (_depPath === toModuleReport.srcPath) {
matchedDependency = true;
break;
}
// Make sure that fromModuleReport dirname has the path separator prepended. This is necessary to make sure
// pathModule (Node.js path) treats `fromModuleReport_dirname` as the absolute root.
if (!fromModuleReport_dirname.startsWith(pathModule.sep)) {
fromModuleReport_dirname = '' + pathModule.sep + fromModuleReport_dirname;
}
if (pathModule.resolve(fromModuleReport_dirname, _depPath) === toModuleReport.srcPath) {
matchedDependency = true;
break;
}
var toModuleReport_modpath = toModuleReport.srcPath;
// Remove any local directory (`.`) leading character from `toModuleReport_modpath`.
if (toModuleReport_modpath.startsWith('.')) {
toModuleReport_modpath = toModuleReport_modpath.replace(/^\./, '');
}
// Ensure `toModuleReport_modpath` starts with the path separator.
if (!toModuleReport_modpath.startsWith(pathModule.sep)) {
toModuleReport_modpath = '' + pathModule.sep + toModuleReport_modpath;
}
if (pathModule.resolve(fromModuleReport_dirname, _depPath) === toModuleReport_modpath) {
matchedDependency = true;
break;
}
}
return matchedDependency;
}
}]);
return PluginMetricsProject;
return PluginMetricsProject;
}();

@@ -364,0 +68,0 @@

{
"name": "escomplex-plugin-metrics-project",
"version": "0.0.10",
"version": "0.0.11",
"homepage": "https://github.com/typhonjs-node-escomplex/escomplex-plugin-metrics-project/",

@@ -23,3 +23,3 @@ "description": "Provides the core project metric / report generation plugin for typhonjs-escomplex project processing.",

"dependencies": {
"typhonjs-escomplex-commons": "^0.0.14"
"typhonjs-escomplex-commons": "^0.0.15"
},

@@ -26,0 +26,0 @@ "devDependencies": {

@@ -1,3 +0,2 @@

import MathUtil from 'typhonjs-escomplex-commons/src/utils/MathUtil';
import ObjectUtil from 'typhonjs-escomplex-commons/src/utils/ObjectUtil';
import ProjectMetricCalculate from './ProjectMetricCalculate';

@@ -13,4 +12,2 @@ /**

{
// ESComplexProject plugin callbacks -----------------------------------------------------------------------------
/**

@@ -42,298 +39,6 @@ * Loads any default settings that are not already provided by any user options.

const projectReport = ev.data.projectReport;
const settings = ev.data.settings;
const adjacencyMatrix = this._calculateAdjacencyMatrix(pathModule, projectReport);
if (!this.settings.noCoreSize)
{
const visibilityMatrix = this._calculateVisibilityMatrix(projectReport, adjacencyMatrix);
this._calculateCoreSize(projectReport, visibilityMatrix);
}
this._calculateAverages(projectReport);
ProjectMetricCalculate.calculate(pathModule, projectReport, settings);
}
/**
* Stores settings.
*
* @param {object} ev - escomplex plugin event data.
*/
onProjectStart(ev)
{
/**
* Stores the settings for all ESComplexProject plugins.
* @type {object}
*/
this.settings = ev.data.settings;
}
// Project metrics calculation -----------------------------------------------------------------------------------
/**
* Calculates an adjacency matrix for all modules based on ES Module and CommonJS dependencies also storing a
* compacted while returning the matrix for further calculation. Each row index corresponds to the same module index.
* Each row entry corresponds to a module index. These relationships dictate the dependencies between all
* module ModuleReports given the source paths.
*
* @param {object} pathModule - A module that conforms to the Node path API.
* @param {object} projectReport - The ProjectResult being processed.
*
* @returns {Array<Array<number>>}
* @private
*/
_calculateAdjacencyMatrix(pathModule, projectReport)
{
const modules = projectReport.modules;
const length = modules.length;
const adjacencyMatrix = MathUtil.create2DArray(length, 0);
let density = 0;
for (let x = 0; x < length; x++)
{
for (let y = 0; y < length; y++)
{
adjacencyMatrix[x][y] = x !== y && this._doesDependencyExist(pathModule, modules[x], modules[y]) ? 1 : 0;
if (adjacencyMatrix[x][y] === 1) { density += 1; }
}
}
projectReport.adjacencyList = MathUtil.compactMatrix(adjacencyMatrix);
projectReport.firstOrderDensity = MathUtil.getPercent(density, length * length);
return adjacencyMatrix;
}
/**
* Calculates average ModuleReport metrics that are applicable to ProjectResult.
*
* @param {object} projectReport - The ProjectResult being processed.
*
* @private
*/
_calculateAverages(projectReport)
{
const divisor = projectReport.modules.length === 0 ? 1 : projectReport.modules.length;
const moduleAverage = projectReport.moduleAverage;
const moduleAverageKeys = ObjectUtil.getAccessorList(moduleAverage);
// Defer to ModuleReport to sum all relevant module metrics applicable to ProjectResult.
projectReport.modules.forEach((module) =>
{
moduleAverageKeys.forEach((averageKey) =>
{
const targetValue = ObjectUtil.safeAccess(module, averageKey, 0);
ObjectUtil.safeSet(moduleAverage, averageKey, targetValue, 'add');
});
});
moduleAverageKeys.forEach((averageKey) =>
{
ObjectUtil.safeSet(moduleAverage, averageKey, divisor, 'div');
});
}
/**
* Calculates core size which is the percentage of modules / files that are both widely depended on and themselves
* depend on other modules. Lower is better.
*
* @param {object} projectReport - The ProjectResult being processed.
* @param {Array<Array<number>>} visibilityMatrix - The calculated visibilityMatrix.
*
* @private
*/
_calculateCoreSize(projectReport, visibilityMatrix)
{
if (projectReport.firstOrderDensity === 0)
{
projectReport.coreSize = 0;
return;
}
const length = visibilityMatrix.length;
const fanIn = new Array(length);
const fanOut = new Array(length);
let coreSize = 0;
for (let rowIndex = 0; rowIndex < length; rowIndex++)
{
fanIn[rowIndex] = visibilityMatrix[rowIndex].reduce((sum, value, valueIndex) =>
{
fanOut[valueIndex] = rowIndex === 0 ? value : fanOut[valueIndex] + value;
return sum + value;
}, 0);
}
// Boundary values can also be chosen by looking for discontinuity in the
// distribution of values, but to keep it simple the median is used.
const boundaries =
{
fanIn: MathUtil.getMedian(fanIn.slice()),
fanOut: MathUtil.getMedian(fanOut.slice())
};
for (let rowIndex = 0; rowIndex < length; rowIndex++)
{
if (fanIn[rowIndex] >= boundaries.fanIn && fanOut[rowIndex] >= boundaries.fanOut) { coreSize += 1; }
}
projectReport.coreSize = MathUtil.getPercent(coreSize, length);
}
/**
* Stores a compacted form of the visibility matrix. Each row index corresponds to the same module index.
* Each row entry corresponds to a module index. These relationships dictate the reverse visibility between all
* module ModuleReports which may indirectly impact the given module / file. The full matrix is returned for further
* calculation.
*
* Implementation of Floyd Warshall algorithm for calculating visibility matrix in O(n^3) instead of O(n^4) with
* successive raising of powers.
*
* @param {object} projectReport - The ProjectResult being processed.
* @param {Array<Array<number>>} adjacencyMatrix - The calculated adjacencyMatrix.
*
* @return {Array<Array<number>>}
* @private
*/
_calculateVisibilityMatrix(projectReport, adjacencyMatrix)
{
let changeCost = 0;
const length = adjacencyMatrix.length;
const visibilityMatrix = MathUtil.create2DArray(length, 0);
// Convert adjacency matrix to a distance matrix suitable for the Floyd Warshall algorithm.
// if i !== j and adjacency matrix value is 0 set distance to Infinity.
for (let x = 0; x < length; x++)
{
for (let y = 0; y < length; y++) { visibilityMatrix[x][y] = x === y ? 1 : adjacencyMatrix[x][y] || Infinity; }
}
// Floyd Warshall core algorithm
for (let k = 0; k < length; k++)
{
for (let x = 0; x < length; x++)
{
for (let y = 0; y < length; y++)
{
if (visibilityMatrix[x][y] > visibilityMatrix[x][k] + visibilityMatrix[k][y])
{
visibilityMatrix[x][y] = visibilityMatrix[x][k] + visibilityMatrix[k][y];
}
}
}
}
// Convert back from a distance matrix to adjacency matrix while also calculating change cost.
for (let x = 0; x < length; x++)
{
for (let y = 0; y < length; y++)
{
if (visibilityMatrix[x][y] < Infinity)
{
changeCost++;
if (x !== y) { visibilityMatrix[x][y] = 1; }
}
else
{
visibilityMatrix[x][y] = 0;
}
}
}
projectReport.visibilityList = MathUtil.compactMatrix(visibilityMatrix);
projectReport.changeCost = MathUtil.getPercent(changeCost, length * length);
return visibilityMatrix;
}
/**
* Determines if there is at least one dependency that matches `toModuleReport.srcPath` from all the dependencies
* stored in `fromModuleReport`.
*
* @param {object} pathModule - A module that conforms to the Node path API.
* @param {ModuleReport} fromModuleReport - A ModuleReport to match to the srcPath of `toModuleReport`.
* @param {ModuleReport} toModuleReport - A ModuleReport providing the `srcPath` to match.
*
* @returns {boolean}
* @private
*/
_doesDependencyExist(pathModule, fromModuleReport, toModuleReport)
{
let matchedDependency = false;
let fromModuleReport_dirname = pathModule.dirname(fromModuleReport.srcPath);
// First test for srcPathAlias which is the case when an NPM or JSPM module has a main entry and is mapped to a
// given name or alias.
for (let cntr = 0; cntr < fromModuleReport.dependencies.length; cntr++)
{
const depPath = fromModuleReport.dependencies[cntr].path;
if (typeof toModuleReport.srcPathAlias === 'string' && depPath === toModuleReport.srcPathAlias)
{
matchedDependency = true;
break;
}
}
// Exit early if alias match was found above.
if (matchedDependency) { return true; }
// Now test for srcPath matches.
for (let cntr = 0; cntr < fromModuleReport.dependencies.length; cntr++)
{
let depPath = fromModuleReport.dependencies[cntr].path;
// If there is no extension provided in the dependency then add the extension of the `to srcPath`.
if (pathModule.extname(depPath) === '') { depPath += pathModule.extname(toModuleReport.srcPath); }
// Best case match scenario when dependency matches toModuleReportPath.srcPath.
if (depPath === toModuleReport.srcPath)
{
matchedDependency = true;
break;
}
// Make sure that fromModuleReport dirname has the path separator prepended. This is necessary to make sure
// pathModule (Node.js path) treats `fromModuleReport_dirname` as the absolute root.
if (!fromModuleReport_dirname.startsWith(pathModule.sep))
{
fromModuleReport_dirname = `${pathModule.sep}${fromModuleReport_dirname}`;
}
if (pathModule.resolve(fromModuleReport_dirname, depPath) === toModuleReport.srcPath)
{
matchedDependency = true;
break;
}
let toModuleReport_modpath = toModuleReport.srcPath;
// Remove any local directory (`.`) leading character from `toModuleReport_modpath`.
if (toModuleReport_modpath.startsWith('.'))
{
toModuleReport_modpath = toModuleReport_modpath.replace(/^\./, '');
}
// Ensure `toModuleReport_modpath` starts with the path separator.
if (!toModuleReport_modpath.startsWith(pathModule.sep))
{
toModuleReport_modpath = `${pathModule.sep}${toModuleReport_modpath}`;
}
if (pathModule.resolve(fromModuleReport_dirname, depPath) === toModuleReport_modpath)
{
matchedDependency = true;
break;
}
}
return matchedDependency;
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc