JavaScript source analysis and visualizer that generates detailed complexity reports
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Data models for tracking code quality metrics over time and maintaining historical analysis data. These models provide the foundation for trend analysis and longitudinal code quality reporting.
Foundation class for tracking analysis data over time with basic operations for adding and retrieving historical entries.
/**
* Base history tracking model for analysis data over time
* @param {Array} data - Initial history data (optional)
*/
class History {
constructor(data);
/** Number of history entries */
length: number;
/**
* Add entry to history
* @param {Object} obj - Object to add to history
*/
push(obj): void;
/**
* Convert history to JSON array
* @returns {Array} JSON representation of history
*/
toJSON(): Array;
}Constructor Parameters:
data (Array, optional): Initial history data to populate the instanceProperties:
length (Number): Number of entries currently stored in historyMethods:
push(obj): Add a new entry to the historytoJSON(): Convert the entire history to a JSON-serializable arrayUsage Examples:
const History = require('plato/lib/models/History');
// Create new history
const history = new History();
// Add entries
history.push({ date: '2024-01-01', complexity: 5.2 });
history.push({ date: '2024-01-02', complexity: 4.8 });
console.log(history.length); // 2
// Convert to JSON
const jsonHistory = history.toJSON();
console.log(jsonHistory); // [{ date: '2024-01-01', complexity: 5.2 }, ...]
// Initialize with existing data
const existingData = [
{ date: '2024-01-01', value: 100 },
{ date: '2024-01-02', value: 95 }
];
const populatedHistory = new History(existingData);
console.log(populatedHistory.length); // 2Specialized history tracking for project-wide overview reports with aggregated metrics across all analyzed files.
/**
* History tracking for overview reports (extends History)
* @param {Array} data - Initial history data (optional)
*/
class OverviewHistory extends History {
constructor(data);
/**
* Add overview report to history with timestamp
* @param {Object} report - Overview report object
* @param {String} date - Report date (optional, defaults to current date)
*/
addReport(report, date): void;
}Constructor Parameters:
data (Array, optional): Initial overview history dataMethods:
addReport(report, date): Add an overview report with automatic timestampingUsage Examples:
const OverviewHistory = require('plato/lib/models/OverviewHistory');
// Create overview history
const overviewHistory = new OverviewHistory();
// Add overview reports
const overviewReport = {
summary: {
total: { sloc: 1000, maintainability: 85.5 },
average: { sloc: 50, maintainability: 85.5 }
},
reports: []
};
overviewHistory.addReport(overviewReport, '2024-01-01');
overviewHistory.addReport(overviewReport, '2024-01-02');
// Track trends over time
const history = overviewHistory.toJSON();
history.forEach(entry => {
console.log(`${entry.date}: ${entry.summary.average.maintainability} maintainability`);
});
// Initialize with existing overview data
const existingOverviews = [
{ date: '2024-01-01', summary: { average: { maintainability: 85 } } }
];
const preloadedHistory = new OverviewHistory(existingOverviews);Specialized history tracking for individual file analysis reports with file-specific metrics over time.
/**
* History tracking for individual file reports (extends History)
* @param {Array} data - Initial history data (optional)
*/
class FileHistory extends History {
constructor(data);
/**
* Add file analysis report to history with timestamp
* @param {Object} report - File analysis report object
* @param {String} date - Report date (optional, defaults to current date)
*/
addReport(report, date): void;
}Constructor Parameters:
data (Array, optional): Initial file history dataMethods:
addReport(report, date): Add a file analysis report with automatic timestampingUsage Examples:
const FileHistory = require('plato/lib/models/FileHistory');
// Create file history
const fileHistory = new FileHistory();
// Add file reports
const fileReport = {
info: { file: 'src/app.js' },
complexity: {
cyclomatic: 8,
maintainability: 82.5,
sloc: { physical: 150, logical: 120 }
}
};
fileHistory.addReport(fileReport, '2024-01-01');
fileHistory.addReport(fileReport, '2024-01-02');
// Analyze file trends
const fileHistoryData = fileHistory.toJSON();
fileHistoryData.forEach(entry => {
console.log(`${entry.date}: Complexity ${entry.complexity.cyclomatic}`);
});
// Load existing file history
const existingFileData = [
{ date: '2024-01-01', complexity: { cyclomatic: 5 } }
];
const existingFileHistory = new FileHistory(existingFileData);These models integrate with Plato's analysis workflow to provide historical tracking:
const plato = require('plato');
const OverviewHistory = require('plato/lib/models/OverviewHistory');
// Load existing overview history
const overviewHistory = new OverviewHistory();
// Perform analysis
plato.inspect(['src/**/*.js'], 'reports', {}, function(reports) {
// Generate overview
const overview = plato.getOverviewReport(reports);
// Add to history
const today = new Date().toISOString().split('T')[0];
overviewHistory.addReport(overview, today);
// Save history for future runs
const historyData = overviewHistory.toJSON();
require('fs').writeFileSync('overview-history.json', JSON.stringify(historyData));
});const plato = require('plato');
const FileHistory = require('plato/lib/models/FileHistory');
// Track individual files over time
const fileHistories = new Map();
plato.inspect(['src/**/*.js'], 'reports', {}, function(reports) {
const today = new Date().toISOString().split('T')[0];
reports.forEach(report => {
const fileName = report.info.file;
// Get or create history for this file
if (!fileHistories.has(fileName)) {
fileHistories.set(fileName, new FileHistory());
}
const fileHistory = fileHistories.get(fileName);
fileHistory.addReport(report, today);
});
// Identify files with increasing complexity
fileHistories.forEach((history, fileName) => {
const entries = history.toJSON();
if (entries.length >= 2) {
const latest = entries[entries.length - 1];
const previous = entries[entries.length - 2];
if (latest.complexity.cyclomatic > previous.complexity.cyclomatic) {
console.log(`${fileName}: Complexity increased from ${previous.complexity.cyclomatic} to ${latest.complexity.cyclomatic}`);
}
}
});
});The models are designed to work with persistent storage for long-term trend analysis:
const OverviewHistory = require('plato/lib/models/OverviewHistory');
const fs = require('fs');
// Save history
function saveHistory(history, filename) {
const data = history.toJSON();
fs.writeFileSync(filename, JSON.stringify(data, null, 2));
}
// Load history
function loadHistory(filename, HistoryClass) {
if (fs.existsSync(filename)) {
const data = JSON.parse(fs.readFileSync(filename, 'utf8'));
return new HistoryClass(data);
}
return new HistoryClass();
}
// Usage
const overviewHistory = loadHistory('overview.json', OverviewHistory);
// ... perform analysis and add reports ...
saveHistory(overviewHistory, 'overview.json');// Example database integration
class DatabaseHistory extends History {
constructor(tableName) {
super();
this.tableName = tableName;
this.loadFromDatabase();
}
async loadFromDatabase() {
// Load history from database
const rows = await db.query(`SELECT * FROM ${this.tableName} ORDER BY date`);
rows.forEach(row => this.push(row));
}
async push(obj) {
super.push(obj);
// Also save to database
await db.query(`INSERT INTO ${this.tableName} VALUES (?, ?)`, [obj.date, JSON.stringify(obj)]);
}
}Common patterns for analyzing historical data:
function analyzeTrends(history) {
const data = history.toJSON();
if (data.length < 2) return null;
const latest = data[data.length - 1];
const previous = data[data.length - 2];
return {
maintainabilityTrend: latest.complexity.maintainability - previous.complexity.maintainability,
complexityTrend: latest.complexity.cyclomatic - previous.complexity.cyclomatic,
slocTrend: latest.complexity.sloc.logical - previous.complexity.sloc.logical
};
}function checkQualityGates(overviewHistory) {
const data = overviewHistory.toJSON();
const latest = data[data.length - 1];
const alerts = [];
if (latest.summary.average.maintainability < 70) {
alerts.push('Low maintainability detected');
}
if (data.length >= 2) {
const previous = data[data.length - 2];
const maintainabilityChange = latest.summary.average.maintainability - previous.summary.average.maintainability;
if (maintainabilityChange < -5) {
alerts.push('Significant maintainability decrease');
}
}
return alerts;
}Install with Tessl CLI
npx tessl i tessl/npm-plato