@pythonidaer/complexity-report 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/assets/prettify.css +1 -0
- package/assets/prettify.js +2 -0
- package/assets/sort-arrow-sprite.png +0 -0
- package/complexity-breakdown.js +53 -0
- package/decision-points/ast-utils.js +127 -0
- package/decision-points/decision-type.js +92 -0
- package/decision-points/function-matching.js +185 -0
- package/decision-points/in-params.js +262 -0
- package/decision-points/index.js +6 -0
- package/decision-points/node-helpers.js +89 -0
- package/decision-points/parent-map.js +62 -0
- package/decision-points/parse-main.js +101 -0
- package/decision-points/ternary-multiline.js +86 -0
- package/export-generators/helpers.js +309 -0
- package/export-generators/index.js +143 -0
- package/export-generators/md-exports.js +160 -0
- package/export-generators/txt-exports.js +262 -0
- package/function-boundaries/arrow-brace-body.js +302 -0
- package/function-boundaries/arrow-helpers.js +93 -0
- package/function-boundaries/arrow-jsx.js +73 -0
- package/function-boundaries/arrow-object-literal.js +65 -0
- package/function-boundaries/arrow-single-expr.js +72 -0
- package/function-boundaries/brace-scanning.js +151 -0
- package/function-boundaries/index.js +67 -0
- package/function-boundaries/named-helpers.js +227 -0
- package/function-boundaries/parse-utils.js +456 -0
- package/function-extraction/ast-utils.js +112 -0
- package/function-extraction/extract-callback.js +65 -0
- package/function-extraction/extract-from-eslint.js +91 -0
- package/function-extraction/extract-name-ast.js +133 -0
- package/function-extraction/extract-name-regex.js +267 -0
- package/function-extraction/index.js +6 -0
- package/function-extraction/utils.js +29 -0
- package/function-hierarchy.js +427 -0
- package/html-generators/about.js +75 -0
- package/html-generators/file-boundary-builders.js +36 -0
- package/html-generators/file-breakdown.js +412 -0
- package/html-generators/file-data.js +50 -0
- package/html-generators/file-helpers.js +100 -0
- package/html-generators/file-javascript.js +430 -0
- package/html-generators/file-line-render.js +160 -0
- package/html-generators/file.css +370 -0
- package/html-generators/file.js +207 -0
- package/html-generators/folder.js +424 -0
- package/html-generators/index.js +6 -0
- package/html-generators/main-index.js +346 -0
- package/html-generators/shared.css +471 -0
- package/html-generators/utils.js +15 -0
- package/index.js +36 -0
- package/integration/eslint/index.js +94 -0
- package/integration/threshold/index.js +45 -0
- package/package.json +64 -0
- package/report/cli.js +58 -0
- package/report/index.js +559 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groups functions by base name and file, keeping the highest complexity version
|
|
3
|
+
* @param {Array} functions - Array of function objects
|
|
4
|
+
* @param {Function} getBaseFunctionName - Function to get base function name
|
|
5
|
+
* @returns {Array} Array of function objects with highest complexity per group
|
|
6
|
+
*/
|
|
7
|
+
function groupFunctionsByBaseName(functions, getBaseFunctionName) {
|
|
8
|
+
const functionGroups = new Map();
|
|
9
|
+
functions.forEach(issue => {
|
|
10
|
+
const baseName = getBaseFunctionName(issue.functionName || 'unknown');
|
|
11
|
+
const key = `${issue.file}:${baseName}`;
|
|
12
|
+
const complexityNum = parseInt(issue.complexity, 10);
|
|
13
|
+
|
|
14
|
+
const existing = functionGroups.get(key);
|
|
15
|
+
if (!existing || complexityNum > parseInt(existing.complexity, 10)) {
|
|
16
|
+
functionGroups.set(key, issue);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return Array.from(functionGroups.values())
|
|
21
|
+
.sort((a, b) => parseInt(b.complexity, 10) - parseInt(a.complexity, 10));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generates HTML row for a function
|
|
26
|
+
* @param {Object} issue - Function object
|
|
27
|
+
* @param {Function} getComplexityLevel - Function to get complexity level
|
|
28
|
+
* @param {Function} getBaseFunctionName - Function to get base function name
|
|
29
|
+
* @param {boolean} showAllInitially - Show all functions initially
|
|
30
|
+
* @returns {string} HTML row string
|
|
31
|
+
*/
|
|
32
|
+
function generateFunctionRow(
|
|
33
|
+
issue,
|
|
34
|
+
getComplexityLevel,
|
|
35
|
+
getBaseFunctionName,
|
|
36
|
+
showAllInitially,
|
|
37
|
+
complexityThreshold = 10
|
|
38
|
+
) {
|
|
39
|
+
const level = getComplexityLevel(issue.complexity);
|
|
40
|
+
const complexityNum = parseInt(issue.complexity, 10);
|
|
41
|
+
const isOverThreshold = complexityNum > complexityThreshold;
|
|
42
|
+
const maxComplexityForBar = Math.max(30, complexityNum);
|
|
43
|
+
const percentage = Math.min(100, (complexityNum / maxComplexityForBar) * 100);
|
|
44
|
+
const fileName = issue.file.split('/').pop();
|
|
45
|
+
const fileLinkPath = `${fileName}.html`;
|
|
46
|
+
const baseFunctionName = getBaseFunctionName(issue.functionName || 'unknown');
|
|
47
|
+
|
|
48
|
+
return `
|
|
49
|
+
<tr class="${level}"
|
|
50
|
+
data-over-threshold="${isOverThreshold}"
|
|
51
|
+
data-file="${issue.file}"
|
|
52
|
+
data-function="${baseFunctionName}"
|
|
53
|
+
data-complexity="${complexityNum}"
|
|
54
|
+
data-line="${issue.line}"
|
|
55
|
+
${!showAllInitially && !isOverThreshold ? 'style="display: none;"' : ''}>
|
|
56
|
+
<td class="file"><a href="${fileLinkPath}">${issue.file}</a></td>
|
|
57
|
+
<td class="bar ${level}">
|
|
58
|
+
<div class="chart">
|
|
59
|
+
<div class="cover-fill ${level} ${percentage === 100 ? 'cover-full' : ''}"
|
|
60
|
+
style="width: ${percentage}%"></div>
|
|
61
|
+
<div class="cover-empty" style="width: ${100 - percentage}%"></div>
|
|
62
|
+
</div>
|
|
63
|
+
</td>
|
|
64
|
+
<td class="file">
|
|
65
|
+
<span style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px;">
|
|
66
|
+
${baseFunctionName}
|
|
67
|
+
</span>
|
|
68
|
+
</td>
|
|
69
|
+
<td class="pct">
|
|
70
|
+
<span class="complexity-value ${level}">${complexityNum}</span>
|
|
71
|
+
</td>
|
|
72
|
+
<td class="abs">${issue.line}</td>
|
|
73
|
+
</tr>
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generates the JavaScript code for folder page functionality
|
|
79
|
+
* @returns {string} JavaScript code as string
|
|
80
|
+
*/
|
|
81
|
+
function generateFolderPageScript() {
|
|
82
|
+
return `(function() {
|
|
83
|
+
function initFilters() {
|
|
84
|
+
// Checkbox filter
|
|
85
|
+
const checkbox = document.getElementById('showAllFunctions');
|
|
86
|
+
const fileSearchInput = document.getElementById('fileSearch');
|
|
87
|
+
const table = document.querySelector('.coverage-summary.function-complexity-table');
|
|
88
|
+
|
|
89
|
+
if (!table) {
|
|
90
|
+
console.warn('Complexity table not found');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const tbody = table.querySelector('tbody');
|
|
95
|
+
if (!tbody) {
|
|
96
|
+
console.warn('Table tbody not found');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function applyFilters() {
|
|
101
|
+
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
102
|
+
const showAll = checkbox ? checkbox.checked : true;
|
|
103
|
+
const searchValue = fileSearchInput ? fileSearchInput.value : '';
|
|
104
|
+
|
|
105
|
+
// Try to create a RegExp from the searchValue
|
|
106
|
+
let searchRegex;
|
|
107
|
+
try {
|
|
108
|
+
searchRegex = searchValue ? new RegExp(searchValue, 'i') : null;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
searchRegex = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
rows.forEach(row => {
|
|
114
|
+
// Check if row matches search filter
|
|
115
|
+
let matchesSearch = true;
|
|
116
|
+
if (searchValue) {
|
|
117
|
+
if (searchRegex) {
|
|
118
|
+
matchesSearch = searchRegex.test(row.textContent);
|
|
119
|
+
} else {
|
|
120
|
+
matchesSearch = row.textContent.toLowerCase().includes(searchValue.toLowerCase());
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check if row matches checkbox filter
|
|
125
|
+
const isOverThreshold = row.getAttribute('data-over-threshold') === 'true';
|
|
126
|
+
const matchesCheckbox = showAll || isOverThreshold;
|
|
127
|
+
|
|
128
|
+
// Show row only if it matches both filters
|
|
129
|
+
if (matchesSearch && matchesCheckbox) {
|
|
130
|
+
// Remove inline style attribute entirely if it only contains display:none
|
|
131
|
+
// Otherwise set display to empty string to show the row
|
|
132
|
+
if (row.getAttribute('style') === 'display: none;') {
|
|
133
|
+
row.removeAttribute('style');
|
|
134
|
+
} else {
|
|
135
|
+
row.style.display = '';
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
row.style.display = 'none';
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (checkbox) {
|
|
144
|
+
checkbox.addEventListener('change', applyFilters);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Filter functionality
|
|
148
|
+
if (fileSearchInput) {
|
|
149
|
+
fileSearchInput.addEventListener('input', applyFilters);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Initialize when DOM is ready
|
|
154
|
+
if (document.readyState === 'loading') {
|
|
155
|
+
document.addEventListener('DOMContentLoaded', initFilters);
|
|
156
|
+
} else {
|
|
157
|
+
initFilters();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Sorting functionality
|
|
161
|
+
const headers = document.querySelectorAll('.coverage-summary th[data-sort]');
|
|
162
|
+
let currentSort = { column: null, direction: 'asc' };
|
|
163
|
+
|
|
164
|
+
function getSortValue(row, column) {
|
|
165
|
+
if (column === 'file') {
|
|
166
|
+
return row.getAttribute('data-file') || '';
|
|
167
|
+
}
|
|
168
|
+
if (column === 'function') {
|
|
169
|
+
return (row.getAttribute('data-function') || '').toLowerCase();
|
|
170
|
+
}
|
|
171
|
+
if (column === 'functions') {
|
|
172
|
+
const parts = (row.getAttribute('data-functions') || '0/0').split('/');
|
|
173
|
+
return parseInt(parts[1] || 1, 10);
|
|
174
|
+
}
|
|
175
|
+
if (column === 'complexity') {
|
|
176
|
+
return parseFloat(row.getAttribute('data-complexity') || 0);
|
|
177
|
+
}
|
|
178
|
+
if (column === 'line') {
|
|
179
|
+
return parseFloat(row.getAttribute('data-line') || 0);
|
|
180
|
+
}
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function compareSortValues(aVal, bVal, direction, column) {
|
|
185
|
+
if (column === 'functions') {
|
|
186
|
+
if (direction === 'desc') {
|
|
187
|
+
return bVal > aVal ? 1 : bVal < aVal ? -1 : 0;
|
|
188
|
+
}
|
|
189
|
+
return aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof aVal === 'string') {
|
|
193
|
+
const comparison = aVal.localeCompare(bVal);
|
|
194
|
+
return direction === 'asc' ? comparison : -comparison;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (direction === 'asc') {
|
|
198
|
+
return aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
|
|
199
|
+
}
|
|
200
|
+
return aVal < bVal ? 1 : aVal > bVal ? -1 : 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
headers.forEach(header => {
|
|
204
|
+
header.addEventListener('click', function() {
|
|
205
|
+
const column = this.getAttribute('data-sort');
|
|
206
|
+
const tbody = this.closest('table').querySelector('tbody');
|
|
207
|
+
const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
208
|
+
|
|
209
|
+
// Set sort column and direction
|
|
210
|
+
if (currentSort.column === column) {
|
|
211
|
+
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
|
|
212
|
+
} else {
|
|
213
|
+
currentSort.column = column;
|
|
214
|
+
currentSort.direction = column === 'functions' ? 'desc' : 'asc';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Update sort classes
|
|
218
|
+
headers.forEach(h => {
|
|
219
|
+
// Remove sorted classes from all headers
|
|
220
|
+
h.classList.remove('sorted', 'sorted-desc');
|
|
221
|
+
// Add appropriate class to the clicked header
|
|
222
|
+
if (h === this) {
|
|
223
|
+
if (currentSort.direction === 'asc') {
|
|
224
|
+
h.classList.add('sorted');
|
|
225
|
+
} else {
|
|
226
|
+
h.classList.add('sorted-desc');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Sort rows
|
|
232
|
+
rows.sort((a, b) => {
|
|
233
|
+
const aVal = getSortValue(a, column);
|
|
234
|
+
const bVal = getSortValue(b, column);
|
|
235
|
+
return compareSortValues(aVal, bVal, currentSort.direction, column);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Re-append sorted rows
|
|
239
|
+
tbody.innerHTML = '';
|
|
240
|
+
rows.forEach(row => tbody.appendChild(row));
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
})();`;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Generates summary section HTML (similar to main index)
|
|
248
|
+
* @param {Object} decisionPointTotals - Object with controlFlow, expressions,
|
|
249
|
+
* functionParameters totals
|
|
250
|
+
* @param {number} totalFunctions - Total number of functions
|
|
251
|
+
* @param {number} withinThreshold - Number of functions within threshold
|
|
252
|
+
* @param {number} withinThresholdPercentage - Percentage within threshold
|
|
253
|
+
* @returns {string} Summary section HTML
|
|
254
|
+
*/
|
|
255
|
+
function generateSummarySection(
|
|
256
|
+
decisionPointTotals,
|
|
257
|
+
totalFunctions,
|
|
258
|
+
withinThreshold,
|
|
259
|
+
_withinThresholdPercentage
|
|
260
|
+
) {
|
|
261
|
+
const { controlFlow, expressions, functionParameters } = decisionPointTotals;
|
|
262
|
+
|
|
263
|
+
// Helper function to format percentage (2 decimals if needed)
|
|
264
|
+
const formatPercentage = (numerator, denominator) => {
|
|
265
|
+
if (denominator === 0) return '0%';
|
|
266
|
+
const percentage = (numerator / denominator) * 100;
|
|
267
|
+
// Whole number: no decimals; otherwise 2 decimal places
|
|
268
|
+
return percentage % 1 === 0
|
|
269
|
+
? `${percentage}%`
|
|
270
|
+
: `${percentage.toFixed(2)}%`;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Functions: show bold % and fraction (can be < 100%)
|
|
274
|
+
// Control Flow / Expressions / Default Parameters: always 100% here
|
|
275
|
+
const functionsPercentage = formatPercentage(
|
|
276
|
+
withinThreshold,
|
|
277
|
+
totalFunctions
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return `
|
|
281
|
+
<div class="clearfix">
|
|
282
|
+
<div class='fl pad1y space-right2'>
|
|
283
|
+
<span class="strong">${functionsPercentage}</span>
|
|
284
|
+
<span class="quiet">Functions</span>
|
|
285
|
+
<span class='fraction'>${withinThreshold}/${totalFunctions}</span>
|
|
286
|
+
</div>
|
|
287
|
+
<div class='fl pad1y space-right2'>
|
|
288
|
+
<span class="quiet">Control Flow</span>
|
|
289
|
+
<span class='fraction'>${controlFlow}</span>
|
|
290
|
+
</div>
|
|
291
|
+
<div class='fl pad1y space-right2'>
|
|
292
|
+
<span class="quiet">Expressions</span>
|
|
293
|
+
<span class='fraction'>${expressions}</span>
|
|
294
|
+
</div>
|
|
295
|
+
<div class='fl pad1y space-right2'>
|
|
296
|
+
<span class="quiet">Default Parameters</span>
|
|
297
|
+
<span class='fraction'>${functionParameters}</span>
|
|
298
|
+
</div>
|
|
299
|
+
</div>`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Calculates status bar level based on percentage
|
|
304
|
+
* @param {number} percentageValue - Percentage value
|
|
305
|
+
* @returns {string} Level string
|
|
306
|
+
*/
|
|
307
|
+
function calculateStatusLevel(percentageValue) {
|
|
308
|
+
if (percentageValue >= 80) return 'high';
|
|
309
|
+
if (percentageValue >= 60) return 'high';
|
|
310
|
+
if (percentageValue >= 40) return 'medium';
|
|
311
|
+
return 'low';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Generates relative path for shared.css
|
|
316
|
+
* @param {string} folderPath - Folder path
|
|
317
|
+
* @returns {string} Relative path to shared.css
|
|
318
|
+
*/
|
|
319
|
+
function getSharedCssPath(folderPath) {
|
|
320
|
+
return folderPath ? '../'.repeat(folderPath.split('/').length) + 'shared.css' : 'shared.css';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Generates back link path
|
|
325
|
+
* @param {string} folderPath - Folder path
|
|
326
|
+
* @returns {string} Back link path
|
|
327
|
+
*/
|
|
328
|
+
function getBackLink(folderPath) {
|
|
329
|
+
return folderPath ? '../'.repeat(folderPath.split('/').length) + 'index.html' : 'index.html';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function generateFolderHTML(
|
|
333
|
+
folder,
|
|
334
|
+
allFolders,
|
|
335
|
+
showAllInitially,
|
|
336
|
+
getComplexityLevel,
|
|
337
|
+
getBaseFunctionName,
|
|
338
|
+
complexityThreshold = 10,
|
|
339
|
+
decisionPointTotals = {
|
|
340
|
+
controlFlow: 0,
|
|
341
|
+
expressions: 0,
|
|
342
|
+
functionParameters: 0,
|
|
343
|
+
}
|
|
344
|
+
) {
|
|
345
|
+
const folderPath = folder.directory;
|
|
346
|
+
const backLink = getBackLink(folderPath);
|
|
347
|
+
const sharedCssPath = getSharedCssPath(folderPath);
|
|
348
|
+
|
|
349
|
+
// Generate summary section
|
|
350
|
+
const summarySection = generateSummarySection(
|
|
351
|
+
decisionPointTotals,
|
|
352
|
+
folder.totalFunctions,
|
|
353
|
+
folder.withinThreshold,
|
|
354
|
+
folder.percentage
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
// Calculate level for status bar
|
|
358
|
+
const percentageValue = folder.totalFunctions > 0
|
|
359
|
+
? (folder.withinThreshold / folder.totalFunctions) * 100
|
|
360
|
+
: 100;
|
|
361
|
+
const level = calculateStatusLevel(percentageValue);
|
|
362
|
+
|
|
363
|
+
return `<!DOCTYPE html>
|
|
364
|
+
<html lang="en">
|
|
365
|
+
<head>
|
|
366
|
+
<meta charset="UTF-8">
|
|
367
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
368
|
+
<title>Complexity Report - ${folderPath || 'Root'}</title>
|
|
369
|
+
<link rel="stylesheet" href="${sharedCssPath}" />
|
|
370
|
+
</head>
|
|
371
|
+
<body>
|
|
372
|
+
<div class="pad2">
|
|
373
|
+
<div class="header-row">
|
|
374
|
+
<h1>${folderPath ? `<a href="${backLink}" style="color: #0074D9; text-decoration: none; font-weight: bold;">All files</a> <span style="font-weight: bold;">${folderPath}</span>` : 'All files'}</h1>
|
|
375
|
+
<a href="${folderPath ? '../'.repeat(folderPath.split('/').length) + 'about.html' : 'about.html'}" class="about-link">About Cyclomatic Complexity</a>
|
|
376
|
+
</div>
|
|
377
|
+
${summarySection}
|
|
378
|
+
<div class="quiet" style="display: flex; align-items: center; gap: 15px; margin-top: 14px;">
|
|
379
|
+
<div>
|
|
380
|
+
Filter:
|
|
381
|
+
<input type="search" id="fileSearch">
|
|
382
|
+
</div>
|
|
383
|
+
<label style="margin: 0; font-weight: normal;">
|
|
384
|
+
<input type="checkbox" id="showAllFunctions" ${showAllInitially ? 'checked' : ''}>
|
|
385
|
+
Show all functions
|
|
386
|
+
</label>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
<div class='status-line ${level}'></div>
|
|
390
|
+
<div class="pad2">
|
|
391
|
+
<table class="coverage-summary function-complexity-table">
|
|
392
|
+
<thead>
|
|
393
|
+
<tr>
|
|
394
|
+
<th class="file" data-sort="file">File <span class="sorter"></span></th>
|
|
395
|
+
<th class="bar" data-sort="complexity" style="text-align: right;"><span class="sorter"></span></th>
|
|
396
|
+
<th class="file" data-sort="function">Function <span class="sorter"></span></th>
|
|
397
|
+
<th class="pct" data-sort="complexity">Complexity <span class="sorter"></span></th>
|
|
398
|
+
<th class="abs" data-sort="line">Line <span class="sorter"></span></th>
|
|
399
|
+
</tr>
|
|
400
|
+
</thead>
|
|
401
|
+
<tbody>
|
|
402
|
+
${groupFunctionsByBaseName(folder.functions, getBaseFunctionName)
|
|
403
|
+
.map((issue) =>
|
|
404
|
+
generateFunctionRow(
|
|
405
|
+
issue,
|
|
406
|
+
getComplexityLevel,
|
|
407
|
+
getBaseFunctionName,
|
|
408
|
+
showAllInitially,
|
|
409
|
+
complexityThreshold
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
.join('')}
|
|
413
|
+
</tbody>
|
|
414
|
+
</table>
|
|
415
|
+
</div>
|
|
416
|
+
<div class='footer quiet pad2 space-top1 center small'>
|
|
417
|
+
Complexity report generated by <a href="https://www.github.com/pythonidaer" target="_blank" rel="noopener noreferrer">pythonidaer</a> at ${new Date().toISOString()}
|
|
418
|
+
</div>
|
|
419
|
+
<script>
|
|
420
|
+
${generateFolderPageScript()}
|
|
421
|
+
</script>
|
|
422
|
+
</body>
|
|
423
|
+
</html>`;
|
|
424
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Re-export all HTML generator functions from their respective modules
|
|
2
|
+
export { escapeHtml } from './utils.js';
|
|
3
|
+
export { generateAboutPageHTML } from './about.js';
|
|
4
|
+
export { generateMainIndexHTML } from './main-index.js';
|
|
5
|
+
export { generateFolderHTML } from './folder.js';
|
|
6
|
+
export { generateFileHTML } from './file.js';
|