typhonjs-escomplex-commons
Version:
Provides core common utilities for typhonjs-escomplex modules and plugins.
300 lines (259 loc) • 10.3 kB
JavaScript
import TransformFormat from '../../transform/TransformFormat';
import ModuleAverage from '../../module/report/averages/ModuleAverage';
import ModuleReport from '../../module/report/ModuleReport';
import ReportType from '../../types/ReportType';
import MathUtil from '../../utils/MathUtil';
import ObjectUtil from '../../utils/ObjectUtil';
import StringUtil from '../../utils/StringUtil';
/**
* Provides the default project report object which stores data pertaining to all modules / files contained.
*
* All modules are stored in the `modules` member variable as ModuleReports.
*
* Various helper methods found in ModuleReport and AbstractReport help increment associated data during collection.
*/
export default class ProjectReport
{
/**
* Returns the enum for the report type.
* @returns {ReportType}
*/
get type() { return ReportType.PROJECT; }
/**
* Initializes ProjectReport with default values.
*
* @param {Array<ModuleReport>} moduleReports - An array of ModuleReports for each module / file processed.
*
* @param {object} settings - An object hash of the settings used in generating this report via
* ESComplexProject.
*/
constructor(moduleReports = void 0, settings = { serializeModules: true })
{
/**
* Stores the settings used to generate the project report.
* @type {object}
*/
this.settings = typeof settings === 'object' ? Object.assign({}, settings) : { serializeModules: true };
/**
* Stores a compacted form of the adjacency matrix. Each row index corresponds to the same report index.
* Each row entry corresponds to a report index. These relationships dictate the dependencies between all
* report ModuleReports given the source paths.
*
* @type {Array<Array<number>>}
*/
this.adjacencyList = void 0;
/**
* Measures the average percentage of modules affected when one module / file in the project is changed.
* Lower is better.
* @type {number}
*/
this.changeCost = 0;
/**
* Measures the percentage of modules that are widely depended on which also depend on other modules.
* Lower is better.
* @type {number}
*/
this.coreSize = 0;
/**
* Stores any analysis errors.
* @type {Array}
*/
this.errors = [];
/**
* Measures the percentage of all possible internal dependencies that are actually realized in the project.
* Lower is better.
* @type {number}
*/
this.firstOrderDensity = 0;
/**
* Stores the average module metric data.
* @type {ModuleAverage}
*/
this.moduleAverage = new ModuleAverage();
/**
* Stores all ModuleReport data for the project sorted by the module / files `srcPath`.
* @type {Array<ModuleReport>}
*/
this.modules = Array.isArray(moduleReports) ?
moduleReports.sort((lhs, rhs) => { return StringUtil.compare(lhs.srcPath, rhs.srcPath); }) : [];
/**
* Stores a compacted form of the visibility matrix. Each row index corresponds to the same report index.
* Each row entry corresponds to a report index. These relationships dictate the reverse visibility between all
* report ModuleReports which may indirectly impact the given module / file.
*
* @type {Array<Array<number>>}
*/
this.visibilityList = void 0;
}
/**
* Clears all errors stored in the project report and by default any module reports.
*
* @param {boolean} clearChildren - (Optional) If false then class and module method errors are not cleared;
* default (true).
*/
clearErrors(clearChildren = true)
{
this.errors = [];
if (clearChildren)
{
this.modules.forEach((report) => { report.clearErrors(); });
}
}
/**
* Finalizes the ProjectReport. If `settings.serializeModules` is false output just `filePath`, `srcPath` &
* `srcPathAlias` entries of modules.
*
* @param {object} options - (Optional) Allows overriding of ModuleReport serialization.
* @property {boolean} serializeModules - Allows overriding of ModuleReport serialization; default: true.
*
* @returns {ProjectReport}
*/
finalize(options = {})
{
if (typeof options !== 'object') { throw new TypeError(`finalize error: 'options' is not an 'object'.`); }
let serializeModules = this.getSetting('serializeModules', true);
// Allow an override opportunity.
if (typeof options.serializeModules === 'boolean') { serializeModules = options.serializeModules; }
if (serializeModules)
{
this.modules.forEach((report) => { report.finalize(); });
}
else
{
this.modules = this.modules.map((report) =>
{
return { filePath: report.filePath, srcPath: report.srcPath, srcPathAlias: report.srcPathAlias };
});
}
return MathUtil.toFixedTraverse(this);
}
/**
* Gets all errors stored in the project report and by default any module reports.
*
* @param {object} options - Optional parameters.
* @property {boolean} includeChildren - If false then module errors are not included; default (true).
* @property {boolean} includeReports - If true then the result will be an array of object hashes containing
* `source` (the source report object of the error) and `error`
* (an AnalyzeError instance) keys and related `module`, `class` entries as;
* default (false).
*
* @returns {Array<AnalyzeError|{error: AnalyzeError, source: *}>}
*/
getErrors(options = { includeChildren: true, includeReports: false })
{
/* istanbul ignore if */
if (typeof options !== 'object') { throw new TypeError(`getErrors error: 'options' is not an 'object'.`); }
// By default set includeChildren to true if not already defined.
/* istanbul ignore if */
if (typeof options.includeChildren !== 'boolean') { options.includeChildren = true; }
// If `includeReports` is true then return an object hash with the source and error otherwise return the error.
let errors = options.includeReports ? this.errors.map((entry) => { return { error: entry, source: this }; }) :
[].concat(...this.errors);
// If `includeChildren` is true then traverse all children reports for errors.
if (options.includeChildren)
{
this.modules.forEach((report) => { errors.push(...report.getErrors(options)); });
}
// If `options.query` is defined then filter errors against the query object.
if (typeof options.query === 'object')
{
errors = errors.filter((error) => ObjectUtil.safeEqual(options.query, error));
}
return errors;
}
/**
* Returns the supported transform formats.
*
* @returns {Object[]}
*/
static getFormats()
{
return TransformFormat.getFormats(ReportType.PROJECT);
}
/**
* Returns the name / id associated with this report.
* @returns {string}
*/
getName()
{
return '';
}
/**
* Returns the setting indexed by the given key.
*
* @param {string} key - A key used to store the setting parameter.
* @param {*} defaultValue - A default value to return if no setting for the given key is currently stored.
*
* @returns {*}
*/
getSetting(key, defaultValue = undefined)
{
/* istanbul ignore if */
if (typeof key !== 'string' || key === '')
{
throw new TypeError(`getSetting error: 'key' is not a 'string' or is empty.`);
}
return typeof this.settings === 'object' && typeof this.settings[key] !== 'undefined' ? this.settings[key] :
defaultValue;
}
/**
* Deserializes a JSON object representing a ProjectReport.
*
* @param {object} object - A JSON object of a ProjectReport that was previously serialized.
*
* @param {object} options - Optional parameters.
* @property {boolean} skipFinalize - If true then automatic finalization is skipped where applicable.
*
* @returns {ProjectReport}
*/
static parse(object, options = { skipFinalize: false })
{
/* istanbul ignore if */
if (typeof object !== 'object') { throw new TypeError(`parse error: 'object' is not an 'object'.`); }
/* istanbul ignore if */
if (typeof options !== 'object') { throw new TypeError(`parse error: 'options' is not an 'object'.`); }
const projectReport = Object.assign(new ProjectReport(), object);
if (projectReport.modules.length > 0)
{
projectReport.modules = projectReport.modules.map((report) => ModuleReport.parse(report));
}
// Must automatically finalize if serializeModules is false.
if (!options.skipFinalize && !projectReport.getSetting('serializeModules', true)) { projectReport.finalize(); }
return projectReport;
}
/**
* Sets the setting indexed by the given key and returns true if successful.
*
* @param {string} key - A key used to store the setting parameter.
* @param {*} value - A value to set to `this.settings[key]`.
*
* @returns {boolean}
*/
setSetting(key, value)
{
/* istanbul ignore if */
if (typeof key !== 'string' || key === '')
{
throw new TypeError(`setSetting error: 'key' is not a 'string' or is empty.`);
}
if (this.settings === 'object')
{
this.settings[key] = value;
return true;
}
return false;
}
/**
* Formats this ProjectReport given the type.
*
* @param {string} name - The name of formatter to use.
*
* @param {object} options - (Optional) One or more optional parameters to pass to the formatter.
*
* @returns {string}
*/
toFormat(name, options = undefined)
{
return TransformFormat.format(this, name, options);
}
}