@georgewrmarshall/design-system-metrics 2.4.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.
@@ -21,7 +21,8 @@
21
21
  "Bash(yarn upgrade:*)",
22
22
  "Bash(yarn up:*)",
23
23
  "Bash(yarn npm info:*)",
24
- "Bash(yarn remove:*)"
24
+ "Bash(yarn remove:*)",
25
+ "Bash(cat:*)"
25
26
  ],
26
27
  "deny": [],
27
28
  "ask": []
package/CHANGELOG.md CHANGED
@@ -5,8 +5,54 @@ 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
+
41
+ ## [2.5.0] - 2026-02-21
42
+
43
+ ### Fixed
44
+
45
+ - **Version Number**: Corrected CLI version number in index.js to match package.json version (was incorrectly set to "3.0.0")
46
+
8
47
  ## [2.4.0] - 2026-02-21
9
48
 
49
+ ### Added
50
+
51
+ - **Output File Date Stamps**: Generated XLSX files now automatically include today's date in the filename
52
+ - Format: `{project}-component-metrics-YYYY-MM-DD.xlsx`
53
+ - Example: `extension-component-metrics-2026-02-21.xlsx`
54
+ - Makes it easier to track metrics over time and compare historical data
55
+
10
56
  ### Changed
11
57
 
12
58
  - **Extension Component Mappings**: Corrected replacement targets to reflect actual migration state
@@ -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
- const XLSX_OUTPUT = path.join(OUTPUT_DIR, 'test-metrics.xlsx');
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 exists = await fs
54
- .access(XLSX_OUTPUT)
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("3.0.0")
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>",
@@ -259,12 +259,19 @@ const main = async () => {
259
259
  rootFolder,
260
260
  ignoreFolders,
261
261
  filePattern,
262
- outputFile,
262
+ outputFile: baseOutputFile,
263
263
  deprecatedComponents = {},
264
264
  currentComponents = [],
265
265
  currentPackages = [],
266
266
  } = projectConfig;
267
267
 
268
+ // Add today's date to the output filename
269
+ const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
270
+ const ext = path.extname(baseOutputFile);
271
+ const basename = path.basename(baseOutputFile, ext);
272
+ const dirname = path.dirname(baseOutputFile);
273
+ const outputFile = path.join(dirname, `${basename}-${today}${ext}`);
274
+
268
275
  const currentComponentsSet = new Set(currentComponents);
269
276
 
270
277
  console.log(chalk.blue(`\nStarting audit for project: ${projectName}\n`));
@@ -362,27 +369,99 @@ const main = async () => {
362
369
  (metrics.replacement.package.includes("@metamask/design-system"))
363
370
  );
364
371
 
372
+ // Group deprecated components by their MMDS replacement component
373
+ const groupedByMMDS = new Map();
365
374
  componentsWithMMDSReplacement.forEach(([componentName, metrics]) => {
366
375
  const mmdsComp = metrics.replacement.component;
367
- const deprecatedCount = metrics.totalCount;
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()) {
368
387
  const mmdsCount = currentMetrics.get(mmdsComp)?.count || 0;
369
- const total = deprecatedCount + mmdsCount;
370
- const percentage = total > 0 ? (mmdsCount / total) * 100 : 0;
371
- const sourcePaths = deprecatedComponents[componentName].paths.join(", ");
372
388
 
373
- migrationSheet.addRow([
374
- componentName,
375
- sourcePaths,
376
- mmdsComp,
377
- deprecatedCount,
378
- mmdsCount,
379
- `${percentage.toFixed(2)}%`,
380
- ]);
381
- });
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
+ ]);
382
458
 
383
459
  console.log(
384
460
  chalk.blue(`Migration Progress: ${componentsWithMMDSReplacement.length} components tracked`)
385
461
  );
462
+ console.log(
463
+ chalk.blue(`Total Migration: ${totalMMDS}/${totalAll} (${totalPercentage.toFixed(2)}%)`)
464
+ );
386
465
 
387
466
  // Sheet 2: Intermediate Migrations (components migrating to component-library)
388
467
  const intermediateSheet = workbook.addWorksheet("Intermediate Migrations");
@@ -400,11 +479,15 @@ const main = async () => {
400
479
  metrics.replacement.package === "component-library"
401
480
  );
402
481
 
482
+ let totalIntermediate = 0;
483
+
403
484
  componentsWithIntermediateReplacement.forEach(([componentName, metrics]) => {
404
485
  const oldPaths = deprecatedComponents[componentName].paths.join(", ");
405
486
  const newComponent = metrics.replacement.component;
406
487
  const newPath = metrics.replacement.path || metrics.replacement.package;
407
488
 
489
+ totalIntermediate += metrics.totalCount;
490
+
408
491
  intermediateSheet.addRow([
409
492
  componentName,
410
493
  oldPaths,
@@ -414,9 +497,21 @@ const main = async () => {
414
497
  ]);
415
498
  });
416
499
 
500
+ // Add totals row
501
+ intermediateSheet.addRow([
502
+ "TOTAL",
503
+ "",
504
+ "",
505
+ "",
506
+ totalIntermediate,
507
+ ]);
508
+
417
509
  console.log(
418
510
  chalk.blue(`Intermediate Migrations: ${componentsWithIntermediateReplacement.length} components tracked`)
419
511
  );
512
+ console.log(
513
+ chalk.blue(`Total Intermediate Instances: ${totalIntermediate}`)
514
+ );
420
515
 
421
516
  // Sheet 3: Path-Level Detail
422
517
  const pathDetailSheet = workbook.addWorksheet("Path-Level Detail");
@@ -446,15 +541,35 @@ const main = async () => {
446
541
  const mmdsSheet = workbook.addWorksheet("MMDS Usage");
447
542
  mmdsSheet.addRow(["Component", "Instances", "File Paths"]);
448
543
 
449
- currentMetrics.forEach((metrics, componentName) => {
450
- console.log(`${chalk.cyan(componentName)}: ${metrics.count} (MMDS)`);
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)`);
451
555
  mmdsSheet.addRow([
452
556
  componentName,
453
- metrics.count,
454
- metrics.files.join(", "),
557
+ count,
558
+ files,
455
559
  ]);
456
560
  });
457
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
+
458
573
  // Sheet 5: No Replacement Components
459
574
  const noReplacementSheet = workbook.addWorksheet("No Replacement");
460
575
  noReplacementSheet.addRow(["Component", "Path", "Instances", "File Paths"]);
@@ -462,8 +577,12 @@ const main = async () => {
462
577
  const componentsWithNoReplacement = Array.from(deprecatedMetrics.entries())
463
578
  .filter(([, metrics]) => !metrics.replacement);
464
579
 
580
+ let totalNoReplacement = 0;
581
+
465
582
  componentsWithNoReplacement.forEach(([componentName, metrics]) => {
466
583
  const paths = deprecatedComponents[componentName].paths.join(", ");
584
+ totalNoReplacement += metrics.totalCount;
585
+
467
586
  noReplacementSheet.addRow([
468
587
  componentName,
469
588
  paths,
@@ -472,9 +591,20 @@ const main = async () => {
472
591
  ]);
473
592
  });
474
593
 
594
+ // Add totals row
595
+ noReplacementSheet.addRow([
596
+ "TOTAL",
597
+ "",
598
+ totalNoReplacement,
599
+ "",
600
+ ]);
601
+
475
602
  console.log(
476
603
  chalk.blue(`No Replacement: ${componentsWithNoReplacement.length} components`)
477
604
  );
605
+ console.log(
606
+ chalk.blue(`Total No Replacement Instances: ${totalNoReplacement}`)
607
+ );
478
608
 
479
609
  // Create output directory if it doesn't exist
480
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.4.0",
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",