@georgewrmarshall/design-system-metrics 2.5.0 → 2.6.0
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/.claude/settings.local.json +2 -1
- package/CHANGELOG.md +33 -0
- package/__tests__/metrics.test.js +22 -7
- package/index.js +141 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.0] - 2026-02-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Complete MMDS Component List**: MMDS Usage sheet now shows all available MMDS components, including those with 0 instances
|
|
13
|
+
- Previously only showed components that were actively used
|
|
14
|
+
- Now displays the full component list with usage counts, making it easier to identify available but unused components
|
|
15
|
+
- Helps track MMDS component adoption opportunities
|
|
16
|
+
|
|
17
|
+
- **Totals Rows**: All sheets now include totals at the bottom for easy summary analysis
|
|
18
|
+
- Migration Progress: Shows total deprecated instances, total MMDS instances, and overall migration percentage (with accurate deduplication)
|
|
19
|
+
- Intermediate Migrations: Shows total instances awaiting intermediate migration
|
|
20
|
+
- MMDS Usage: Shows total MMDS component instances across all components
|
|
21
|
+
- No Replacement: Shows total instances of components without direct replacements
|
|
22
|
+
- Console output now displays these totals for quick visibility
|
|
23
|
+
|
|
24
|
+
- **Grouped Display with Summary Rows**: Migration Progress sheet now groups deprecated components by their MMDS replacement
|
|
25
|
+
- Components mapping to the same MMDS component are displayed together (e.g., all Icon variants grouped together)
|
|
26
|
+
- Individual rows show deprecated component details with "-" for MMDS instances and percentage to avoid misleading duplicates
|
|
27
|
+
- Summary rows (format: "→ Component Total") show aggregated deprecated count, MMDS instances, and accurate migration percentage for each group
|
|
28
|
+
- Single-mapping components continue to display normally with all columns filled
|
|
29
|
+
- Provides much more accurate and meaningful migration percentages
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Migration Progress Totals**: Fixed duplicate counting when multiple deprecated components map to the same MMDS component
|
|
34
|
+
- Each MMDS component is counted only once in the total, providing accurate migration percentage
|
|
35
|
+
- Individual rows still show correct per-component breakdown
|
|
36
|
+
|
|
37
|
+
- **Test Suite**: Updated tests to work with date-stamped output files
|
|
38
|
+
- Added helper function to dynamically find generated XLSX files
|
|
39
|
+
- All 14 tests passing
|
|
40
|
+
|
|
8
41
|
## [2.5.0] - 2026-02-21
|
|
9
42
|
|
|
10
43
|
### Fixed
|
|
@@ -5,7 +5,13 @@ const ExcelJS = require('exceljs');
|
|
|
5
5
|
|
|
6
6
|
const TEST_CONFIG = path.join(__dirname, 'fixtures/test-config.json');
|
|
7
7
|
const OUTPUT_DIR = path.join(__dirname, 'output');
|
|
8
|
-
|
|
8
|
+
// Helper to get the generated XLSX file (with date stamp)
|
|
9
|
+
const getXLSXOutput = async () => {
|
|
10
|
+
const files = await fs.readdir(OUTPUT_DIR);
|
|
11
|
+
const xlsxFile = files.find(f => f.startsWith('test-metrics-') && f.endsWith('.xlsx'));
|
|
12
|
+
return xlsxFile ? path.join(OUTPUT_DIR, xlsxFile) : null;
|
|
13
|
+
};
|
|
14
|
+
const XLSX_OUTPUT_BASE = 'test-metrics';
|
|
9
15
|
|
|
10
16
|
describe('Design System Metrics', () => {
|
|
11
17
|
beforeAll(() => {
|
|
@@ -50,12 +56,8 @@ describe('Design System Metrics', () => {
|
|
|
50
56
|
{ stdio: 'pipe' }
|
|
51
57
|
);
|
|
52
58
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
.then(() => true)
|
|
56
|
-
.catch(() => false);
|
|
57
|
-
|
|
58
|
-
expect(exists).toBe(true);
|
|
59
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
60
|
+
expect(XLSX_OUTPUT).not.toBeNull();
|
|
59
61
|
|
|
60
62
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
61
63
|
const sheetNames = workbook.worksheets.map(ws => ws.name);
|
|
@@ -74,6 +76,7 @@ describe('Design System Metrics', () => {
|
|
|
74
76
|
{ stdio: 'pipe' }
|
|
75
77
|
);
|
|
76
78
|
|
|
79
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
77
80
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
78
81
|
const data = getSheetData(workbook, 'Migration Progress');
|
|
79
82
|
const headers = data[0];
|
|
@@ -92,6 +95,7 @@ describe('Design System Metrics', () => {
|
|
|
92
95
|
{ stdio: 'pipe' }
|
|
93
96
|
);
|
|
94
97
|
|
|
98
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
95
99
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
96
100
|
const data = getSheetData(workbook, 'Migration Progress');
|
|
97
101
|
|
|
@@ -111,6 +115,7 @@ describe('Design System Metrics', () => {
|
|
|
111
115
|
{ stdio: 'pipe' }
|
|
112
116
|
);
|
|
113
117
|
|
|
118
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
114
119
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
115
120
|
const data = getSheetData(workbook, 'Migration Progress');
|
|
116
121
|
|
|
@@ -128,6 +133,7 @@ describe('Design System Metrics', () => {
|
|
|
128
133
|
{ stdio: 'pipe' }
|
|
129
134
|
);
|
|
130
135
|
|
|
136
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
131
137
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
132
138
|
const data = getSheetData(workbook, 'Path-Level Detail');
|
|
133
139
|
const headers = data[0];
|
|
@@ -144,6 +150,7 @@ describe('Design System Metrics', () => {
|
|
|
144
150
|
{ stdio: 'pipe' }
|
|
145
151
|
);
|
|
146
152
|
|
|
153
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
147
154
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
148
155
|
const data = getSheetData(workbook, 'Path-Level Detail');
|
|
149
156
|
|
|
@@ -159,6 +166,7 @@ describe('Design System Metrics', () => {
|
|
|
159
166
|
{ stdio: 'pipe' }
|
|
160
167
|
);
|
|
161
168
|
|
|
169
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
162
170
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
163
171
|
const data = getSheetData(workbook, 'Path-Level Detail');
|
|
164
172
|
|
|
@@ -174,6 +182,7 @@ describe('Design System Metrics', () => {
|
|
|
174
182
|
{ stdio: 'pipe' }
|
|
175
183
|
);
|
|
176
184
|
|
|
185
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
177
186
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
178
187
|
const data = getSheetData(workbook, 'Path-Level Detail');
|
|
179
188
|
|
|
@@ -190,6 +199,7 @@ describe('Design System Metrics', () => {
|
|
|
190
199
|
{ stdio: 'pipe' }
|
|
191
200
|
);
|
|
192
201
|
|
|
202
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
193
203
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
194
204
|
const data = getSheetData(workbook, 'MMDS Usage');
|
|
195
205
|
const headers = data[0];
|
|
@@ -205,6 +215,7 @@ describe('Design System Metrics', () => {
|
|
|
205
215
|
{ stdio: 'pipe' }
|
|
206
216
|
);
|
|
207
217
|
|
|
218
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
208
219
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
209
220
|
const data = getSheetData(workbook, 'MMDS Usage');
|
|
210
221
|
|
|
@@ -222,6 +233,7 @@ describe('Design System Metrics', () => {
|
|
|
222
233
|
{ stdio: 'pipe' }
|
|
223
234
|
);
|
|
224
235
|
|
|
236
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
225
237
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
226
238
|
const data = getSheetData(workbook, 'MMDS Usage');
|
|
227
239
|
|
|
@@ -237,6 +249,7 @@ describe('Design System Metrics', () => {
|
|
|
237
249
|
{ stdio: 'pipe' }
|
|
238
250
|
);
|
|
239
251
|
|
|
252
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
240
253
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
241
254
|
const data = getSheetData(workbook, 'MMDS Usage');
|
|
242
255
|
|
|
@@ -253,6 +266,7 @@ describe('Design System Metrics', () => {
|
|
|
253
266
|
{ stdio: 'pipe' }
|
|
254
267
|
);
|
|
255
268
|
|
|
269
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
256
270
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
257
271
|
const data = getSheetData(workbook, 'No Replacement');
|
|
258
272
|
const headers = data[0];
|
|
@@ -269,6 +283,7 @@ describe('Design System Metrics', () => {
|
|
|
269
283
|
{ stdio: 'pipe' }
|
|
270
284
|
);
|
|
271
285
|
|
|
286
|
+
const XLSX_OUTPUT = await getXLSXOutput();
|
|
272
287
|
const workbook = await readXLSX(XLSX_OUTPUT);
|
|
273
288
|
const data = getSheetData(workbook, 'No Replacement');
|
|
274
289
|
|
package/index.js
CHANGED
|
@@ -46,7 +46,7 @@ const validateConfig = (cfg) => {
|
|
|
46
46
|
|
|
47
47
|
// Define CLI options using Commander
|
|
48
48
|
program
|
|
49
|
-
.version("2.
|
|
49
|
+
.version("2.6.0")
|
|
50
50
|
.description("Design System Metrics CLI Tool - Track component usage and migration progress")
|
|
51
51
|
.requiredOption(
|
|
52
52
|
"-p, --project <name>",
|
|
@@ -369,27 +369,99 @@ const main = async () => {
|
|
|
369
369
|
(metrics.replacement.package.includes("@metamask/design-system"))
|
|
370
370
|
);
|
|
371
371
|
|
|
372
|
+
// Group deprecated components by their MMDS replacement component
|
|
373
|
+
const groupedByMMDS = new Map();
|
|
372
374
|
componentsWithMMDSReplacement.forEach(([componentName, metrics]) => {
|
|
373
375
|
const mmdsComp = metrics.replacement.component;
|
|
374
|
-
|
|
376
|
+
if (!groupedByMMDS.has(mmdsComp)) {
|
|
377
|
+
groupedByMMDS.set(mmdsComp, []);
|
|
378
|
+
}
|
|
379
|
+
groupedByMMDS.get(mmdsComp).push([componentName, metrics]);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
let totalDeprecated = 0;
|
|
383
|
+
let totalMMDS = 0;
|
|
384
|
+
|
|
385
|
+
// Process each MMDS component group
|
|
386
|
+
for (const [mmdsComp, deprecatedComponents] of groupedByMMDS.entries()) {
|
|
375
387
|
const mmdsCount = currentMetrics.get(mmdsComp)?.count || 0;
|
|
376
|
-
const total = deprecatedCount + mmdsCount;
|
|
377
|
-
const percentage = total > 0 ? (mmdsCount / total) * 100 : 0;
|
|
378
|
-
const sourcePaths = deprecatedComponents[componentName].paths.join(", ");
|
|
379
388
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
deprecatedCount
|
|
385
|
-
mmdsCount
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
+
if (deprecatedComponents.length === 1) {
|
|
390
|
+
// Single component mapping - show normally
|
|
391
|
+
const [componentName, metrics] = deprecatedComponents[0];
|
|
392
|
+
const deprecatedCount = metrics.totalCount;
|
|
393
|
+
const total = deprecatedCount + mmdsCount;
|
|
394
|
+
const percentage = total > 0 ? (mmdsCount / total) * 100 : 0;
|
|
395
|
+
const sourcePaths = config.projects[projectName].deprecatedComponents[componentName].paths.join(", ");
|
|
396
|
+
|
|
397
|
+
totalDeprecated += deprecatedCount;
|
|
398
|
+
totalMMDS += mmdsCount;
|
|
399
|
+
|
|
400
|
+
migrationSheet.addRow([
|
|
401
|
+
componentName,
|
|
402
|
+
sourcePaths,
|
|
403
|
+
mmdsComp,
|
|
404
|
+
deprecatedCount,
|
|
405
|
+
mmdsCount,
|
|
406
|
+
`${percentage.toFixed(2)}%`,
|
|
407
|
+
]);
|
|
408
|
+
} else {
|
|
409
|
+
// Multiple components mapping to same MMDS component
|
|
410
|
+
let groupDeprecatedTotal = 0;
|
|
411
|
+
|
|
412
|
+
// Add individual rows (without MMDS count and percentage)
|
|
413
|
+
deprecatedComponents.forEach(([componentName, metrics]) => {
|
|
414
|
+
const deprecatedCount = metrics.totalCount;
|
|
415
|
+
const sourcePaths = config.projects[projectName].deprecatedComponents[componentName].paths.join(", ");
|
|
416
|
+
|
|
417
|
+
groupDeprecatedTotal += deprecatedCount;
|
|
418
|
+
|
|
419
|
+
migrationSheet.addRow([
|
|
420
|
+
componentName,
|
|
421
|
+
sourcePaths,
|
|
422
|
+
mmdsComp,
|
|
423
|
+
deprecatedCount,
|
|
424
|
+
"-",
|
|
425
|
+
"-",
|
|
426
|
+
]);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Add summary row for the group
|
|
430
|
+
const total = groupDeprecatedTotal + mmdsCount;
|
|
431
|
+
const percentage = total > 0 ? (mmdsCount / total) * 100 : 0;
|
|
432
|
+
|
|
433
|
+
migrationSheet.addRow([
|
|
434
|
+
`→ ${mmdsComp} Total`,
|
|
435
|
+
"",
|
|
436
|
+
mmdsComp,
|
|
437
|
+
groupDeprecatedTotal,
|
|
438
|
+
mmdsCount,
|
|
439
|
+
`${percentage.toFixed(2)}%`,
|
|
440
|
+
]);
|
|
441
|
+
|
|
442
|
+
totalDeprecated += groupDeprecatedTotal;
|
|
443
|
+
totalMMDS += mmdsCount;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Add totals row
|
|
448
|
+
const totalAll = totalDeprecated + totalMMDS;
|
|
449
|
+
const totalPercentage = totalAll > 0 ? (totalMMDS / totalAll) * 100 : 0;
|
|
450
|
+
migrationSheet.addRow([
|
|
451
|
+
"TOTAL",
|
|
452
|
+
"",
|
|
453
|
+
"",
|
|
454
|
+
totalDeprecated,
|
|
455
|
+
totalMMDS,
|
|
456
|
+
`${totalPercentage.toFixed(2)}%`,
|
|
457
|
+
]);
|
|
389
458
|
|
|
390
459
|
console.log(
|
|
391
460
|
chalk.blue(`Migration Progress: ${componentsWithMMDSReplacement.length} components tracked`)
|
|
392
461
|
);
|
|
462
|
+
console.log(
|
|
463
|
+
chalk.blue(`Total Migration: ${totalMMDS}/${totalAll} (${totalPercentage.toFixed(2)}%)`)
|
|
464
|
+
);
|
|
393
465
|
|
|
394
466
|
// Sheet 2: Intermediate Migrations (components migrating to component-library)
|
|
395
467
|
const intermediateSheet = workbook.addWorksheet("Intermediate Migrations");
|
|
@@ -407,11 +479,15 @@ const main = async () => {
|
|
|
407
479
|
metrics.replacement.package === "component-library"
|
|
408
480
|
);
|
|
409
481
|
|
|
482
|
+
let totalIntermediate = 0;
|
|
483
|
+
|
|
410
484
|
componentsWithIntermediateReplacement.forEach(([componentName, metrics]) => {
|
|
411
485
|
const oldPaths = deprecatedComponents[componentName].paths.join(", ");
|
|
412
486
|
const newComponent = metrics.replacement.component;
|
|
413
487
|
const newPath = metrics.replacement.path || metrics.replacement.package;
|
|
414
488
|
|
|
489
|
+
totalIntermediate += metrics.totalCount;
|
|
490
|
+
|
|
415
491
|
intermediateSheet.addRow([
|
|
416
492
|
componentName,
|
|
417
493
|
oldPaths,
|
|
@@ -421,9 +497,21 @@ const main = async () => {
|
|
|
421
497
|
]);
|
|
422
498
|
});
|
|
423
499
|
|
|
500
|
+
// Add totals row
|
|
501
|
+
intermediateSheet.addRow([
|
|
502
|
+
"TOTAL",
|
|
503
|
+
"",
|
|
504
|
+
"",
|
|
505
|
+
"",
|
|
506
|
+
totalIntermediate,
|
|
507
|
+
]);
|
|
508
|
+
|
|
424
509
|
console.log(
|
|
425
510
|
chalk.blue(`Intermediate Migrations: ${componentsWithIntermediateReplacement.length} components tracked`)
|
|
426
511
|
);
|
|
512
|
+
console.log(
|
|
513
|
+
chalk.blue(`Total Intermediate Instances: ${totalIntermediate}`)
|
|
514
|
+
);
|
|
427
515
|
|
|
428
516
|
// Sheet 3: Path-Level Detail
|
|
429
517
|
const pathDetailSheet = workbook.addWorksheet("Path-Level Detail");
|
|
@@ -453,15 +541,35 @@ const main = async () => {
|
|
|
453
541
|
const mmdsSheet = workbook.addWorksheet("MMDS Usage");
|
|
454
542
|
mmdsSheet.addRow(["Component", "Instances", "File Paths"]);
|
|
455
543
|
|
|
456
|
-
|
|
457
|
-
|
|
544
|
+
// Include all MMDS components, even those with 0 instances
|
|
545
|
+
let totalMMDSUsage = 0;
|
|
546
|
+
|
|
547
|
+
currentComponentsSet.forEach((componentName) => {
|
|
548
|
+
const metrics = currentMetrics.get(componentName);
|
|
549
|
+
const count = metrics ? metrics.count : 0;
|
|
550
|
+
const files = metrics ? metrics.files.join(", ") : "";
|
|
551
|
+
|
|
552
|
+
totalMMDSUsage += count;
|
|
553
|
+
|
|
554
|
+
console.log(`${chalk.cyan(componentName)}: ${count} (MMDS)`);
|
|
458
555
|
mmdsSheet.addRow([
|
|
459
556
|
componentName,
|
|
460
|
-
|
|
461
|
-
|
|
557
|
+
count,
|
|
558
|
+
files,
|
|
462
559
|
]);
|
|
463
560
|
});
|
|
464
561
|
|
|
562
|
+
// Add totals row
|
|
563
|
+
mmdsSheet.addRow([
|
|
564
|
+
"TOTAL",
|
|
565
|
+
totalMMDSUsage,
|
|
566
|
+
"",
|
|
567
|
+
]);
|
|
568
|
+
|
|
569
|
+
console.log(
|
|
570
|
+
chalk.blue(`Total MMDS Usage: ${totalMMDSUsage} instances`)
|
|
571
|
+
);
|
|
572
|
+
|
|
465
573
|
// Sheet 5: No Replacement Components
|
|
466
574
|
const noReplacementSheet = workbook.addWorksheet("No Replacement");
|
|
467
575
|
noReplacementSheet.addRow(["Component", "Path", "Instances", "File Paths"]);
|
|
@@ -469,8 +577,12 @@ const main = async () => {
|
|
|
469
577
|
const componentsWithNoReplacement = Array.from(deprecatedMetrics.entries())
|
|
470
578
|
.filter(([, metrics]) => !metrics.replacement);
|
|
471
579
|
|
|
580
|
+
let totalNoReplacement = 0;
|
|
581
|
+
|
|
472
582
|
componentsWithNoReplacement.forEach(([componentName, metrics]) => {
|
|
473
583
|
const paths = deprecatedComponents[componentName].paths.join(", ");
|
|
584
|
+
totalNoReplacement += metrics.totalCount;
|
|
585
|
+
|
|
474
586
|
noReplacementSheet.addRow([
|
|
475
587
|
componentName,
|
|
476
588
|
paths,
|
|
@@ -479,9 +591,20 @@ const main = async () => {
|
|
|
479
591
|
]);
|
|
480
592
|
});
|
|
481
593
|
|
|
594
|
+
// Add totals row
|
|
595
|
+
noReplacementSheet.addRow([
|
|
596
|
+
"TOTAL",
|
|
597
|
+
"",
|
|
598
|
+
totalNoReplacement,
|
|
599
|
+
"",
|
|
600
|
+
]);
|
|
601
|
+
|
|
482
602
|
console.log(
|
|
483
603
|
chalk.blue(`No Replacement: ${componentsWithNoReplacement.length} components`)
|
|
484
604
|
);
|
|
605
|
+
console.log(
|
|
606
|
+
chalk.blue(`Total No Replacement Instances: ${totalNoReplacement}`)
|
|
607
|
+
);
|
|
485
608
|
|
|
486
609
|
// Create output directory if it doesn't exist
|
|
487
610
|
const outputDir = path.dirname(outputFile);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@georgewrmarshall/design-system-metrics",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "A CLI tool to audit design system component usage from local libraries, NPM packages, and track deprecated components across MetaMask codebases",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"packageManager": "yarn@4.3.1",
|