@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 +30 -1
- package/package.json +1 -1
- package/scripts/generate-usage-report.cjs +274 -90
- package/scripts/README.md +0 -119
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 [
|
|
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
|
@@ -125,14 +125,15 @@ function parseFileForImports(filePath) {
|
|
|
125
125
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
126
126
|
const imports = [];
|
|
127
127
|
|
|
128
|
-
|
|
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
|
-
|
|
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:
|
|
146
|
+
line: getLineNumber(match.index)
|
|
146
147
|
});
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
// Match default imports: import Component from '
|
|
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
|
-
|
|
157
|
+
for (const match of content.matchAll(defaultImportRegex)) {
|
|
157
158
|
imports.push({
|
|
158
159
|
name: match[1],
|
|
159
160
|
type: 'default',
|
|
160
|
-
line:
|
|
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.
|
|
238
|
-
|
|
239
|
-
|
|
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 `| ${
|
|
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
|
-
|
|
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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
265
|
-
|
|
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}: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
326
|
-
const
|
|
327
|
-
|
|
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
|
-
|
|
413
|
+
if (!categorized.has(category)) {
|
|
414
|
+
categorized.set(category, []);
|
|
415
|
+
}
|
|
416
|
+
categorized.get(category).push({ name, usages });
|
|
332
417
|
});
|
|
333
418
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
339
|
-
lines.push(
|
|
454
|
+
lines.push(`*Last updated: ${dateStr}* `);
|
|
455
|
+
lines.push(`*Generated by ${PACKAGE_NAME} usage report tool*`);
|
|
340
456
|
lines.push('');
|
|
341
|
-
|
|
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
|
-
|
|
344
|
-
lines.push(`${index + 1}. **${name}** - ${usages.length} usage${usages.length > 1 ? 's' : ''}`);
|
|
345
|
-
});
|
|
477
|
+
const usageMap = scanStoryFiles();
|
|
346
478
|
|
|
347
|
-
|
|
348
|
-
lines.push('---');
|
|
349
|
-
lines.push('');
|
|
479
|
+
console.log(` Found ${usageMap.size} unique components used in stories`);
|
|
350
480
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
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
|