@civicactions/cmsds-open-data-components 4.0.8-alpha.0 → 4.0.8-alpha.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/README.md CHANGED
@@ -62,7 +62,36 @@ This creates `COMPONENTS_INVENTORY.md` in the root directory with:
62
62
 
63
63
  The inventory is automatically updated on every commit via a pre-commit hook.
64
64
 
65
- For more details, see the [Inventory Generator Documentation](scripts/README.md).
65
+ For more details, see the [Scripts Documentation](scripts/README.md).
66
+
67
+ ## Component Usage Report
68
+
69
+ This project includes a script that can be run in projects that use cmsds-open-data-components as a dependency to analyze component usage.
70
+
71
+ ### Running in Dependent Projects
72
+
73
+ From a project that has `@civicactions/cmsds-open-data-components` as a dependency:
74
+
75
+ ```bash
76
+ npx generate-usage-report
77
+ ```
78
+
79
+ This generates `COMPONENT_USAGE_REPORT.md` showing:
80
+ - Which components from the library are being used
81
+ - Where each component is imported in the project
82
+ - Summary statistics and category breakdown
83
+ - GitHub links to component source code
84
+
85
+ ### Generating Sample Report (Library Development)
86
+
87
+ To generate a sample report using Storybook files in this repository:
88
+ ```bash
89
+ npm run generate:usage-report
90
+ ```
91
+
92
+ This creates `SAMPLE_COMPONENT_USAGE_REPORT.md` demonstrating the report format.
93
+
94
+ For more details, see the [Scripts Documentation](scripts/README.md).
66
95
 
67
96
  ## Publishing new versions
68
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civicactions/cmsds-open-data-components",
3
- "version": "4.0.8-alpha.0",
3
+ "version": "4.0.8-alpha.2",
4
4
  "description": "Components for the open data catalog frontend using CMS Design System",
5
5
  "main": "dist/main.js",
6
6
  "source": "src/index.ts",
@@ -125,14 +125,15 @@ function parseFileForImports(filePath) {
125
125
  const content = fs.readFileSync(filePath, 'utf8');
126
126
  const imports = [];
127
127
 
128
- // Match ES6 imports: import { Component1, Component2 } from '@civicactions/cmsds-open-data-components'
128
+ const getLineNumber = (index) => content.substring(0, index).split('\n').length;
129
+
130
+ // Match named imports: import { Component1, Component2 } from 'package'
129
131
  const namedImportRegex = new RegExp(
130
132
  `import\\s+\\{([^}]+)\\}\\s+from\\s+['"]${PACKAGE_NAME}['"]`,
131
133
  'g'
132
134
  );
133
135
 
134
- let match;
135
- while ((match = namedImportRegex.exec(content)) !== null) {
136
+ for (const match of content.matchAll(namedImportRegex)) {
136
137
  const names = match[1]
137
138
  .split(',')
138
139
  .map(n => n.trim())
@@ -142,22 +143,22 @@ function parseFileForImports(filePath) {
142
143
  imports.push({
143
144
  name,
144
145
  type: 'named',
145
- line: content.substring(0, match.index).split('\n').length
146
+ line: getLineNumber(match.index)
146
147
  });
147
148
  });
148
149
  }
149
150
 
150
- // Match default imports: import Component from '@civicactions/cmsds-open-data-components'
151
+ // Match default imports: import Component from 'package'
151
152
  const defaultImportRegex = new RegExp(
152
153
  `import\\s+(\\w+)\\s+from\\s+['"]${PACKAGE_NAME}['"]`,
153
154
  'g'
154
155
  );
155
156
 
156
- while ((match = defaultImportRegex.exec(content)) !== null) {
157
+ for (const match of content.matchAll(defaultImportRegex)) {
157
158
  imports.push({
158
159
  name: match[1],
159
160
  type: 'default',
160
- line: content.substring(0, match.index).split('\n').length
161
+ line: getLineNumber(match.index)
161
162
  });
162
163
  }
163
164
 
@@ -229,16 +230,52 @@ function categorizeComponent(name) {
229
230
  }
230
231
 
231
232
  /**
232
- * Generate markdown table row
233
+ * Generate markdown table row with GitHub link
233
234
  */
234
235
  function generateUsageRow(name, usages, category) {
235
236
  const count = usages.length;
236
237
  const files = [...new Set(usages.map(u => u.file))];
237
- const fileList = files.length <= 3
238
- ? files.map(f => `\`${f}\``).join(', ')
239
- : `${files.slice(0, 3).map(f => `\`${f}\``).join(', ')} and ${files.length - 3} more`;
238
+ const fileList = files.map(f => `\`${f}\``).join('<br>');
239
+
240
+ const githubPath = getGitHubPath(name, category);
241
+ const githubUrl = `https://github.com/GetDKAN/cmsds-open-data-components/tree/main/${githubPath}`;
242
+ const nameLink = `[${name}](${githubUrl})`;
240
243
 
241
- return `| ${name} | ${category} | ${count} | ${fileList} |`;
244
+ return `| ${nameLink} | ${count} | ${fileList} |`;
245
+ }
246
+
247
+ /**
248
+ * Determine GitHub path based on component category and name
249
+ */
250
+ function getGitHubPath(name, category) {
251
+ // Standard categories map directly to directories
252
+ const categoryPaths = {
253
+ 'Component': `src/components/${name}`,
254
+ 'Hook': `src/components/${name}`,
255
+ 'Template': `src/templates/${name}`,
256
+ 'Service': `src/services/${name}`,
257
+ 'Utility': `src/utilities/${name}`
258
+ };
259
+
260
+ if (categoryPaths[category]) {
261
+ return categoryPaths[category];
262
+ }
263
+
264
+ // Context locations vary, check by name
265
+ if (category === 'Context') {
266
+ if (name.includes('DataTable')) {
267
+ return `src/components/DatasetTableTab/${name}.tsx`;
268
+ }
269
+ if (name === 'HeaderContext') {
270
+ return `src/templates/Header/${name}.tsx`;
271
+ }
272
+ if (name === 'ACAContext') {
273
+ return `src/utilities/${name}.ts`;
274
+ }
275
+ return `src/templates/Dataset/${name}.tsx`;
276
+ }
277
+
278
+ return `src/components/${name}`;
242
279
  }
243
280
 
244
281
  /**
@@ -246,33 +283,67 @@ function generateUsageRow(name, usages, category) {
246
283
  */
247
284
  function generateReport(usageMap, availableComponents) {
248
285
  const lines = [];
286
+ const projectInfo = getProjectInfo(availableComponents);
287
+
288
+ // Header
289
+ addReportHeader(lines, projectInfo);
290
+
291
+ // Summary statistics
292
+ addSummaryStatistics(lines, usageMap);
293
+
294
+ // Categorize and add category breakdown
295
+ const categorized = categorizeUsages(usageMap);
296
+ addCategoryBreakdown(lines, categorized);
297
+
298
+ // Detailed usage table
299
+ addUsageTable(lines, usageMap);
300
+
301
+ // Footer
302
+ addReportFooter(lines);
249
303
 
250
- // Get project info
304
+ return lines.join('\n');
305
+ }
306
+
307
+ /**
308
+ * Get project information
309
+ */
310
+ function getProjectInfo(availableComponents) {
251
311
  const projectPkgPath = path.join(projectRoot, 'package.json');
252
- let projectName = 'Unknown Project';
253
- let projectVersion = '';
254
312
 
255
- if (fs.existsSync(projectPkgPath)) {
256
- const projectPkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf8'));
257
- projectName = projectPkg.name || path.basename(projectRoot);
258
-
259
- // Get the installed version
260
- const deps = { ...projectPkg.dependencies, ...projectPkg.devDependencies };
261
- projectVersion = deps[PACKAGE_NAME] || availableComponents.version;
313
+ if (!fs.existsSync(projectPkgPath)) {
314
+ return {
315
+ name: 'Unknown Project',
316
+ version: availableComponents.version
317
+ };
262
318
  }
263
319
 
264
- // Header
265
- lines.push(`# ${projectName} Component Usage Report`);
320
+ const projectPkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf8'));
321
+ const deps = { ...projectPkg.dependencies, ...projectPkg.devDependencies };
322
+
323
+ return {
324
+ name: projectPkg.name || path.basename(projectRoot),
325
+ version: deps[PACKAGE_NAME] || availableComponents.version
326
+ };
327
+ }
328
+
329
+ /**
330
+ * Add report header section
331
+ */
332
+ function addReportHeader(lines, projectInfo) {
333
+ lines.push(`# ${projectInfo.name} Component Usage Report`);
266
334
  lines.push('');
267
335
  lines.push(`Analysis of \`${PACKAGE_NAME}\` usage in this project.`);
268
336
  lines.push('');
269
- lines.push(`**Library Version**: \`${PACKAGE_NAME}: ${projectVersion}\` `);
270
- lines.push(`**Generated**: ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`);
337
+ lines.push(`**Library Version**: \`${PACKAGE_NAME}: ${projectInfo.version}\` `);
271
338
  lines.push('');
272
339
  lines.push('---');
273
340
  lines.push('');
274
-
275
- // Summary statistics
341
+ }
342
+
343
+ /**
344
+ * Add summary statistics section
345
+ */
346
+ function addSummaryStatistics(lines, usageMap) {
276
347
  const totalUsages = Array.from(usageMap.values()).reduce((sum, usages) => sum + usages.length, 0);
277
348
  const uniqueComponents = usageMap.size;
278
349
  const filesUsingComponents = new Set();
@@ -287,9 +358,14 @@ function generateReport(usageMap, availableComponents) {
287
358
  lines.push(`- **Total Import Statements**: ${totalUsages}`);
288
359
  lines.push(`- **Files Using Components**: ${filesUsingComponents.size}`);
289
360
  lines.push('');
290
-
291
- // Categorize components
361
+ }
362
+
363
+ /**
364
+ * Categorize components by type
365
+ */
366
+ function categorizeUsages(usageMap) {
292
367
  const categorized = new Map();
368
+
293
369
  usageMap.forEach((usages, name) => {
294
370
  const category = categorizeComponent(name);
295
371
  if (!categorized.has(category)) {
@@ -298,7 +374,13 @@ function generateReport(usageMap, availableComponents) {
298
374
  categorized.get(category).push({ name, usages });
299
375
  });
300
376
 
301
- // Category breakdown
377
+ return categorized;
378
+ }
379
+
380
+ /**
381
+ * Add category breakdown section
382
+ */
383
+ function addCategoryBreakdown(lines, categorized) {
302
384
  lines.push('### By Category');
303
385
  lines.push('');
304
386
  lines.push('| Category | Count |');
@@ -315,80 +397,168 @@ function generateReport(usageMap, availableComponents) {
315
397
  lines.push('');
316
398
  lines.push('---');
317
399
  lines.push('');
318
-
319
- // Detailed usage table
400
+ }
401
+
402
+ /**
403
+ * Add detailed usage table section grouped by category
404
+ */
405
+ function addUsageTable(lines, usageMap) {
320
406
  lines.push('## Component Usage Details');
321
407
  lines.push('');
322
- lines.push('| Component | Type | Usage Count | Used In |');
323
- lines.push('|-----------|------|-------------|---------|');
324
408
 
325
- // Sort by usage count (descending)
326
- const sortedComponents = Array.from(usageMap.entries())
327
- .sort((a, b) => b[1].length - a[1].length);
328
-
329
- sortedComponents.forEach(([name, usages]) => {
409
+ // Categorize all components
410
+ const categorized = new Map();
411
+ usageMap.forEach((usages, name) => {
330
412
  const category = categorizeComponent(name);
331
- lines.push(generateUsageRow(name, usages, category));
413
+ if (!categorized.has(category)) {
414
+ categorized.set(category, []);
415
+ }
416
+ categorized.get(category).push({ name, usages });
332
417
  });
333
418
 
334
- lines.push('');
335
- lines.push('---');
336
- lines.push('');
419
+ // Generate a table for each category
420
+ const categories = ['Component', 'Template', 'Hook', 'Context', 'Service', 'Utility'];
421
+ categories.forEach(category => {
422
+ const items = categorized.get(category);
423
+ if (!items || items.length === 0) return;
424
+
425
+ // Sort by usage count within category
426
+ items.sort((a, b) => b.usages.length - a.usages.length);
427
+
428
+ // Category header
429
+ lines.push(`### ${category}s`);
430
+ lines.push('');
431
+
432
+ // Table header
433
+ lines.push('| Component | Count | Used In |');
434
+ lines.push('|-----------|-------|---------|');
435
+
436
+ // Table rows
437
+ items.forEach(({ name, usages }) => {
438
+ lines.push(generateUsageRow(name, usages, category));
439
+ });
440
+
441
+ lines.push('');
442
+ lines.push('---');
443
+ lines.push('');
444
+ });
445
+ }
446
+
447
+ /**
448
+ * Add report footer
449
+ */
450
+ function addReportFooter(lines) {
451
+ const now = new Date();
452
+ const dateStr = now.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
337
453
 
338
- // Most used components
339
- lines.push('## Most Used Components');
454
+ lines.push(`*Last updated: ${dateStr}* `);
455
+ lines.push(`*Generated by ${PACKAGE_NAME} usage report tool*`);
340
456
  lines.push('');
341
- const topComponents = sortedComponents.slice(0, 10);
457
+ }
458
+
459
+ /**
460
+ * Check if running in the library repo itself
461
+ */
462
+ function isLibraryRepo() {
463
+ const pkgJsonPath = path.join(projectRoot, 'package.json');
464
+ if (fs.existsSync(pkgJsonPath)) {
465
+ const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
466
+ return pkgJson.name === PACKAGE_NAME;
467
+ }
468
+ return false;
469
+ }
470
+
471
+ /**
472
+ * Generate sample report for the library repo using Storybook files
473
+ */
474
+ function generateSampleReport() {
475
+ console.log('šŸ“š Detected library repository - generating sample usage report...\n');
342
476
 
343
- topComponents.forEach(([name, usages], index) => {
344
- lines.push(`${index + 1}. **${name}** - ${usages.length} usage${usages.length > 1 ? 's' : ''}`);
345
- });
477
+ const usageMap = scanStoryFiles();
346
478
 
347
- lines.push('');
348
- lines.push('---');
349
- lines.push('');
479
+ console.log(` Found ${usageMap.size} unique components used in stories`);
350
480
 
351
- // Detailed file listings
352
- lines.push('## Detailed File Usage');
353
- lines.push('');
481
+ const pkgJson = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
482
+ const availableComponents = {
483
+ version: pkgJson.version,
484
+ exports: new Set()
485
+ };
354
486
 
355
- categorized.forEach((items, category) => {
356
- if (items.length > 0) {
357
- lines.push(`### ${category}s`);
358
- lines.push('');
359
-
360
- items.sort((a, b) => a.name.localeCompare(b.name));
361
-
362
- items.forEach(({ name, usages }) => {
363
- lines.push(`#### ${name}`);
364
- lines.push('');
365
-
366
- // Group by file
367
- const fileGroups = new Map();
368
- usages.forEach(usage => {
369
- if (!fileGroups.has(usage.file)) {
370
- fileGroups.set(usage.file, []);
371
- }
372
- fileGroups.get(usage.file).push(usage);
373
- });
374
-
375
- fileGroups.forEach((fileUsages, file) => {
376
- const lineNumbers = fileUsages.map(u => u.line).join(', ');
377
- lines.push(`- \`${file}\` (line${fileUsages.length > 1 ? 's' : ''} ${lineNumbers})`);
378
- });
379
-
380
- lines.push('');
381
- });
382
- }
487
+ const report = generateReport(usageMap, availableComponents);
488
+ const sampleNote = createSampleNote();
489
+
490
+ return sampleNote + report;
491
+ }
492
+
493
+ /**
494
+ * Scan Storybook files for component usage
495
+ */
496
+ function scanStoryFiles() {
497
+ const usageMap = new Map();
498
+ const srcPath = path.join(projectRoot, 'src');
499
+ const storyFiles = findFiles(srcPath, ['.stories.js', '.stories.jsx', '.stories.ts', '.stories.tsx']);
500
+
501
+ console.log(` Found ${storyFiles.length} Storybook files to analyze`);
502
+
503
+ storyFiles.forEach(file => {
504
+ const relativePath = path.relative(projectRoot, file);
505
+ const content = fs.readFileSync(file, 'utf8');
506
+
507
+ extractLocalImports(content, relativePath, usageMap);
383
508
  });
384
509
 
385
- lines.push('---');
386
- lines.push('');
387
- lines.push(`*Generated by ${PACKAGE_NAME} usage report tool* `);
388
- lines.push(`*Report generated: ${new Date().toISOString()}*`);
389
- lines.push('');
510
+ return usageMap;
511
+ }
512
+
513
+ /**
514
+ * Extract local imports from file content
515
+ */
516
+ function extractLocalImports(content, relativePath, usageMap) {
517
+ const getLineNumber = (index) => content.substring(0, index).split('\n').length;
390
518
 
391
- return lines.join('\n');
519
+ // Match local imports like: import { Component } from './Component'
520
+ const localImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]\.\//g;
521
+ const defaultImportRegex = /import\s+(\w+)\s+from\s+['"]\.\//g;
522
+
523
+ for (const match of content.matchAll(localImportRegex)) {
524
+ const names = match[1].split(',').map(n => n.trim()).filter(n => n.length > 0);
525
+ names.forEach(name => {
526
+ if (!usageMap.has(name)) {
527
+ usageMap.set(name, []);
528
+ }
529
+ usageMap.get(name).push({
530
+ file: relativePath,
531
+ line: getLineNumber(match.index),
532
+ type: 'named'
533
+ });
534
+ });
535
+ }
536
+
537
+ for (const match of content.matchAll(defaultImportRegex)) {
538
+ const name = match[1];
539
+ if (!usageMap.has(name)) {
540
+ usageMap.set(name, []);
541
+ }
542
+ usageMap.get(name).push({
543
+ file: relativePath,
544
+ line: getLineNumber(match.index),
545
+ type: 'default'
546
+ });
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Create sample note for library repo reports
552
+ */
553
+ function createSampleNote() {
554
+ return [
555
+ '> **Note**: This is a sample usage report generated from the cmsds-open-data-components library repository.',
556
+ '> It shows how components are used in Storybook files within this repository.',
557
+ '> When run in a project that depends on this library, the report will show actual component usage.',
558
+ '',
559
+ '---',
560
+ ''
561
+ ].join('\n');
392
562
  }
393
563
 
394
564
  /**
@@ -398,6 +568,20 @@ function main() {
398
568
  console.log('šŸ“Š Generating Component Usage Report...\n');
399
569
 
400
570
  try {
571
+ // Check if running in the library repo itself
572
+ if (isLibraryRepo()) {
573
+ const report = generateSampleReport();
574
+ const outputPath = path.join(projectRoot, 'SAMPLE_' + outputFile);
575
+ fs.writeFileSync(outputPath, report, 'utf8');
576
+
577
+ console.log(`\nāœ… Sample usage report generated successfully!`);
578
+ console.log(`šŸ“„ Output: ${outputPath}`);
579
+ console.log(`šŸ“Š File size: ${(Buffer.byteLength(report, 'utf8') / 1024).toFixed(2)} KB`);
580
+ console.log(`\nšŸ’” This sample report shows component usage in Storybook files.`);
581
+ console.log(` To see real usage, run this command in a project that depends on this library.`);
582
+ return;
583
+ }
584
+
401
585
  // Check if package is installed
402
586
  const packagePath = path.join(projectRoot, 'node_modules', PACKAGE_NAME);
403
587
  if (!fs.existsSync(packagePath)) {
package/scripts/README.md DELETED
@@ -1,119 +0,0 @@
1
- # Component Inventory Generator
2
-
3
- This script automatically generates a comprehensive markdown inventory report for the cmsds-open-data-components library.
4
-
5
- ## Usage
6
-
7
- Run the script using npm:
8
-
9
- ```bash
10
- npm run generate:inventory
11
- ```
12
-
13
- Or run it directly:
14
-
15
- ```bash
16
- node scripts/generate-inventory.cjs
17
- ```
18
-
19
- ## Output
20
-
21
- The script generates `COMPONENTS_INVENTORY.md` in the root directory containing:
22
-
23
- - **Inventory Table**: Complete list of all components, services, templates, hooks, contexts, utilities, types, and assets
24
- - **Public Export Status**: Indicates which items are publicly exported vs internal-only
25
- - **Storybook Status**: Shows which items have Storybook stories for visual documentation
26
- - **Unit Test Status**: Shows which items have unit test coverage
27
- - **Quality Metrics**: Summary statistics for documentation and testing coverage
28
- - **Export Summary**: Count of public vs internal items by category
29
-
30
- ## What It Scans
31
-
32
- The script automatically scans the following directories:
33
-
34
- - `src/components/` - React components
35
- - `src/templates/` - Page-level templates
36
- - `src/services/` - API service hooks
37
- - `src/utilities/` - Utility functions
38
- - `src/types/` - TypeScript type definitions
39
- - `src/assets/` - Static assets and data files
40
-
41
- It also automatically detects:
42
- - **Hooks**: Directories or files starting with `use` (e.g., `useScrollToTop`)
43
- - **Contexts**: Files ending with `Context` that contain `createContext()` calls
44
-
45
- ## How It Works
46
-
47
- 1. **Reads `src/index.ts`** to determine which items are publicly exported
48
- 2. **Scans each directory** for subdirectories and files
49
- 3. **Dynamically discovers hooks and contexts** by naming patterns and file content
50
- 4. **Checks for `.stories.*` files** to determine Storybook coverage
51
- 5. **Checks for `.test.*` and `.spec.*` files** to determine unit test coverage
52
- 6. **Calculates statistics** for overall project quality metrics
53
- 7. **Generates markdown** with formatted tables and summaries
54
-
55
- ## Maintenance
56
-
57
- The script is designed to automatically adapt to changes in the codebase:
58
-
59
- - New components/templates/services/hooks/contexts are automatically discovered
60
- - Public export status updates when `src/index.ts` changes
61
- - Story and test status updates as files are added/removed
62
- - Statistics recalculate automatically
63
-
64
- No manual updates needed when adding or removing hooks and contexts!
65
-
66
- ### Hook and Context Detection
67
-
68
- **Hooks** are detected by:
69
- - Directory names starting with `use` (e.g., `src/components/useScrollToTop/`)
70
- - File names starting with `use` (e.g., `useAddLoginLink.ts`)
71
-
72
- **Contexts** are detected by:
73
- - File names ending with `Context` (e.g., `HeaderContext.tsx`)
74
- - File must contain a `createContext()` call
75
- - Export name is extracted from `export default` or `export const` statements
76
-
77
- The scanner automatically excludes test and story files to prevent false positives.
78
-
79
- ### Known Special Cases
80
-
81
- The script handles several special cases:
82
-
83
- - **DatasetAdditionalInformation**: Exports `buildRows` function
84
- - **DatasetTableTab**: Exported as `DatasetTable`
85
- - **Datatable**: Exported as `DataTable` (case difference)
86
- - **frequencyMap**: Commented out in exports
87
- - **aca.ts**: Exports `acaToParams` function
88
-
89
- To add new special cases, update the scanning functions in `generate-inventory.cjs`.
90
-
91
- ## File Structure
92
-
93
- ```
94
- scripts/
95
- generate-inventory.cjs # Main script file
96
- README.md # This file
97
-
98
- COMPONENTS_INVENTORY.md # Generated output file (root directory)
99
- ```
100
-
101
- ## Requirements
102
-
103
- - Node.js v14 or higher
104
- - No additional dependencies required (uses Node.js built-in `fs` and `path` modules)
105
-
106
- ## Troubleshooting
107
-
108
- **Error: "require is not defined"**
109
- - The script uses `.cjs` extension to work with the ES module package type
110
- - Make sure the file is named `generate-inventory.cjs` (not `.js`)
111
-
112
- **Missing components in output**
113
- - Ensure new components follow the standard directory structure
114
- - Component directories should be in `src/components/`
115
- - Each component should have an `index.tsx` or similar entry file
116
-
117
- **Incorrect public export status**
118
- - Check if the component is exported in `src/index.ts`
119
- - For special export names, add them to the scanning logic in the script