@civicactions/cmsds-open-data-components 4.0.8-alpha.0 ā 4.0.8-alpha.1
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/README.md +149 -4
- package/scripts/generate-usage-report.cjs +241 -79
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
package/scripts/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Scripts Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains utility scripts for managing and analyzing the cmsds-open-data-components library.
|
|
4
|
+
|
|
5
|
+
## Component Inventory Generator
|
|
2
6
|
|
|
3
7
|
This script automatically generates a comprehensive markdown inventory report for the cmsds-open-data-components library.
|
|
4
8
|
|
|
@@ -92,12 +96,153 @@ To add new special cases, update the scanning functions in `generate-inventory.c
|
|
|
92
96
|
|
|
93
97
|
```
|
|
94
98
|
scripts/
|
|
95
|
-
generate-inventory.cjs
|
|
96
|
-
|
|
99
|
+
generate-inventory.cjs # Component inventory script
|
|
100
|
+
generate-inventory.test.js # Tests for inventory script
|
|
101
|
+
generate-usage-report.cjs # Component usage report script
|
|
102
|
+
generate-usage-report.test.js # Tests for usage report script
|
|
103
|
+
README.md # This file
|
|
104
|
+
|
|
105
|
+
COMPONENTS_INVENTORY.md # Generated inventory (root directory)
|
|
106
|
+
COMPONENT_USAGE_REPORT.md # Generated usage report (root directory, when run in dependent projects)
|
|
107
|
+
SAMPLE_COMPONENT_USAGE_REPORT.md # Sample usage report (root directory, when run locally)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
# Component Usage Report Generator
|
|
113
|
+
|
|
114
|
+
This script analyzes projects that use cmsds-open-data-components as a dependency and generates a comprehensive report showing which components are being used and where.
|
|
115
|
+
|
|
116
|
+
## Usage
|
|
97
117
|
|
|
98
|
-
|
|
118
|
+
### In Projects Using cmsds-open-data-components
|
|
119
|
+
|
|
120
|
+
From a project that has `@civicactions/cmsds-open-data-components` as a dependency:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npx generate-usage-report
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Or add to your project's scripts in `package.json`:
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"scripts": {
|
|
130
|
+
"usage-report": "generate-usage-report"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then run:
|
|
136
|
+
```bash
|
|
137
|
+
npm run usage-report
|
|
99
138
|
```
|
|
100
139
|
|
|
140
|
+
### In the Library Repository
|
|
141
|
+
|
|
142
|
+
Run the script directly in this repository to generate a sample report:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run generate:usage-report
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or run it directly:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
node scripts/generate-usage-report.cjs
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
When run in the library repository, it generates a sample report (`SAMPLE_COMPONENT_USAGE_REPORT.md`) by analyzing Storybook files.
|
|
155
|
+
|
|
156
|
+
## Output
|
|
157
|
+
|
|
158
|
+
The script generates a markdown report containing:
|
|
159
|
+
|
|
160
|
+
- **Library Version**: Version of cmsds-open-data-components being analyzed
|
|
161
|
+
- **Summary Statistics**: Total unique components, import statements, and files analyzed
|
|
162
|
+
- **Category Breakdown**: Components grouped by type (Component, Template, Hook, Context, Service, Utility)
|
|
163
|
+
- **Component Usage Details**: Table showing each component with:
|
|
164
|
+
- Component name (linked to GitHub repository)
|
|
165
|
+
- Category/type
|
|
166
|
+
- Number of times imported
|
|
167
|
+
- List of files where it's used
|
|
168
|
+
|
|
169
|
+
## What It Scans
|
|
170
|
+
|
|
171
|
+
The script scans the following directories in your project:
|
|
172
|
+
|
|
173
|
+
- `src/` - Main source directory
|
|
174
|
+
- `app/` - Next.js app directory
|
|
175
|
+
- `pages/` - Next.js pages directory
|
|
176
|
+
- `components/` - Components directory
|
|
177
|
+
- `templates/` - Templates directory
|
|
178
|
+
|
|
179
|
+
It searches for these file types:
|
|
180
|
+
- `.js`, `.jsx` - JavaScript/React files
|
|
181
|
+
- `.ts`, `.tsx` - TypeScript/React files
|
|
182
|
+
|
|
183
|
+
The script automatically excludes:
|
|
184
|
+
- `node_modules/` - Dependencies
|
|
185
|
+
- `dist/`, `build/` - Build output directories
|
|
186
|
+
- `.next/`, `.cache/` - Framework cache directories
|
|
187
|
+
|
|
188
|
+
## How It Works
|
|
189
|
+
|
|
190
|
+
1. **Detects execution context**: Determines if running in the library repo or a dependent project
|
|
191
|
+
2. **Scans project files**: Recursively searches for JavaScript/TypeScript files
|
|
192
|
+
3. **Parses imports**: Uses regex to find imports from `@civicactions/cmsds-open-data-components`
|
|
193
|
+
4. **Tracks usage**: Records each component, where it's imported, and at what line number
|
|
194
|
+
5. **Categorizes components**: Automatically categorizes by naming patterns:
|
|
195
|
+
- Hooks: Names starting with `use`
|
|
196
|
+
- Contexts: Names ending with `Context`
|
|
197
|
+
- Templates: From `templates/` directory
|
|
198
|
+
- Services: From `services/` directory
|
|
199
|
+
- Utilities: From `utilities/` directory
|
|
200
|
+
- Components: Everything else
|
|
201
|
+
6. **Generates report**: Creates formatted markdown with statistics and detailed usage tables
|
|
202
|
+
7. **Adds GitHub links**: Links each component name to its source in the GitHub repository
|
|
203
|
+
|
|
204
|
+
## Component Categories
|
|
205
|
+
|
|
206
|
+
Components are automatically categorized based on naming conventions and source location:
|
|
207
|
+
|
|
208
|
+
- **Hook**: Components starting with `use` (e.g., `useScrollToTop`, `useDatastore`)
|
|
209
|
+
- **Context**: Components ending with `Context` (e.g., `ACAContext`)
|
|
210
|
+
- **Template**: Components from the `templates/` directory
|
|
211
|
+
- **Service**: Components from the `services/` directory
|
|
212
|
+
- **Utility**: Components from the `utilities/` directory
|
|
213
|
+
- **Component**: All other React components
|
|
214
|
+
|
|
215
|
+
## Sample vs Real Reports
|
|
216
|
+
|
|
217
|
+
**In the library repository**: Generates `SAMPLE_COMPONENT_USAGE_REPORT.md` by analyzing how components are used in Storybook files. This serves as an example of the report format.
|
|
218
|
+
|
|
219
|
+
**In dependent projects**: Generates `COMPONENT_USAGE_REPORT.md` by analyzing actual component usage throughout the project's codebase.
|
|
220
|
+
|
|
221
|
+
## Requirements
|
|
222
|
+
|
|
223
|
+
- Node.js v14 or higher
|
|
224
|
+
- No additional dependencies required (uses Node.js built-in `fs` and `path` modules)
|
|
225
|
+
|
|
226
|
+
## Troubleshooting
|
|
227
|
+
|
|
228
|
+
**Error: "require is not defined"**
|
|
229
|
+
- The script uses `.cjs` extension to work with the ES module package type
|
|
230
|
+
- Make sure the file is named `generate-usage-report.cjs` (not `.js`)
|
|
231
|
+
|
|
232
|
+
**No components found in scan**
|
|
233
|
+
- Verify that `@civicactions/cmsds-open-data-components` is installed as a dependency
|
|
234
|
+
- Check that your import statements use the full package name
|
|
235
|
+
- Ensure source files are in scanned directories (`src/`, `app/`, `pages/`, `components/`, `templates/`)
|
|
236
|
+
|
|
237
|
+
**Missing some component usages**
|
|
238
|
+
- The script only detects standard ES6 import syntax
|
|
239
|
+
- Dynamic imports (e.g., `import()`) are not currently detected
|
|
240
|
+
- Ensure imports use the package name: `from '@civicactions/cmsds-open-data-components'`
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## File Structure
|
|
245
|
+
|
|
101
246
|
## Requirements
|
|
102
247
|
|
|
103
248
|
- Node.js v14 or higher
|
|
@@ -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} | ${category} | ${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);
|
|
249
297
|
|
|
250
|
-
//
|
|
298
|
+
// Detailed usage table
|
|
299
|
+
addUsageTable(lines, usageMap);
|
|
300
|
+
|
|
301
|
+
// Footer
|
|
302
|
+
addReportFooter(lines);
|
|
303
|
+
|
|
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,8 +397,12 @@ 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
|
|
404
|
+
*/
|
|
405
|
+
function addUsageTable(lines, usageMap) {
|
|
320
406
|
lines.push('## Component Usage Details');
|
|
321
407
|
lines.push('');
|
|
322
408
|
lines.push('| Component | Type | Usage Count | Used In |');
|
|
@@ -334,61 +420,123 @@ function generateReport(usageMap, availableComponents) {
|
|
|
334
420
|
lines.push('');
|
|
335
421
|
lines.push('---');
|
|
336
422
|
lines.push('');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Add report footer
|
|
427
|
+
*/
|
|
428
|
+
function addReportFooter(lines) {
|
|
429
|
+
const now = new Date();
|
|
430
|
+
const dateStr = now.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
|
337
431
|
|
|
338
|
-
|
|
339
|
-
lines.push(
|
|
432
|
+
lines.push(`*Last updated: ${dateStr}* `);
|
|
433
|
+
lines.push(`*Generated by ${PACKAGE_NAME} usage report tool*`);
|
|
340
434
|
lines.push('');
|
|
341
|
-
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Check if running in the library repo itself
|
|
439
|
+
*/
|
|
440
|
+
function isLibraryRepo() {
|
|
441
|
+
const pkgJsonPath = path.join(projectRoot, 'package.json');
|
|
442
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
443
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
|
|
444
|
+
return pkgJson.name === PACKAGE_NAME;
|
|
445
|
+
}
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Generate sample report for the library repo using Storybook files
|
|
451
|
+
*/
|
|
452
|
+
function generateSampleReport() {
|
|
453
|
+
console.log('š Detected library repository - generating sample usage report...\n');
|
|
342
454
|
|
|
343
|
-
|
|
344
|
-
lines.push(`${index + 1}. **${name}** - ${usages.length} usage${usages.length > 1 ? 's' : ''}`);
|
|
345
|
-
});
|
|
455
|
+
const usageMap = scanStoryFiles();
|
|
346
456
|
|
|
347
|
-
|
|
348
|
-
lines.push('---');
|
|
349
|
-
lines.push('');
|
|
457
|
+
console.log(` Found ${usageMap.size} unique components used in stories`);
|
|
350
458
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
459
|
+
const pkgJson = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
460
|
+
const availableComponents = {
|
|
461
|
+
version: pkgJson.version,
|
|
462
|
+
exports: new Set()
|
|
463
|
+
};
|
|
354
464
|
|
|
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
|
-
}
|
|
465
|
+
const report = generateReport(usageMap, availableComponents);
|
|
466
|
+
const sampleNote = createSampleNote();
|
|
467
|
+
|
|
468
|
+
return sampleNote + report;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Scan Storybook files for component usage
|
|
473
|
+
*/
|
|
474
|
+
function scanStoryFiles() {
|
|
475
|
+
const usageMap = new Map();
|
|
476
|
+
const srcPath = path.join(projectRoot, 'src');
|
|
477
|
+
const storyFiles = findFiles(srcPath, ['.stories.js', '.stories.jsx', '.stories.ts', '.stories.tsx']);
|
|
478
|
+
|
|
479
|
+
console.log(` Found ${storyFiles.length} Storybook files to analyze`);
|
|
480
|
+
|
|
481
|
+
storyFiles.forEach(file => {
|
|
482
|
+
const relativePath = path.relative(projectRoot, file);
|
|
483
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
484
|
+
|
|
485
|
+
extractLocalImports(content, relativePath, usageMap);
|
|
383
486
|
});
|
|
384
487
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
488
|
+
return usageMap;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Extract local imports from file content
|
|
493
|
+
*/
|
|
494
|
+
function extractLocalImports(content, relativePath, usageMap) {
|
|
495
|
+
const getLineNumber = (index) => content.substring(0, index).split('\n').length;
|
|
390
496
|
|
|
391
|
-
|
|
497
|
+
// Match local imports like: import { Component } from './Component'
|
|
498
|
+
const localImportRegex = /import\s+\{([^}]+)\}\s+from\s+['"]\.\//g;
|
|
499
|
+
const defaultImportRegex = /import\s+(\w+)\s+from\s+['"]\.\//g;
|
|
500
|
+
|
|
501
|
+
for (const match of content.matchAll(localImportRegex)) {
|
|
502
|
+
const names = match[1].split(',').map(n => n.trim()).filter(n => n.length > 0);
|
|
503
|
+
names.forEach(name => {
|
|
504
|
+
if (!usageMap.has(name)) {
|
|
505
|
+
usageMap.set(name, []);
|
|
506
|
+
}
|
|
507
|
+
usageMap.get(name).push({
|
|
508
|
+
file: relativePath,
|
|
509
|
+
line: getLineNumber(match.index),
|
|
510
|
+
type: 'named'
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
for (const match of content.matchAll(defaultImportRegex)) {
|
|
516
|
+
const name = match[1];
|
|
517
|
+
if (!usageMap.has(name)) {
|
|
518
|
+
usageMap.set(name, []);
|
|
519
|
+
}
|
|
520
|
+
usageMap.get(name).push({
|
|
521
|
+
file: relativePath,
|
|
522
|
+
line: getLineNumber(match.index),
|
|
523
|
+
type: 'default'
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Create sample note for library repo reports
|
|
530
|
+
*/
|
|
531
|
+
function createSampleNote() {
|
|
532
|
+
return [
|
|
533
|
+
'> **Note**: This is a sample usage report generated from the cmsds-open-data-components library repository.',
|
|
534
|
+
'> It shows how components are used in Storybook files within this repository.',
|
|
535
|
+
'> When run in a project that depends on this library, the report will show actual component usage.',
|
|
536
|
+
'',
|
|
537
|
+
'---',
|
|
538
|
+
''
|
|
539
|
+
].join('\n');
|
|
392
540
|
}
|
|
393
541
|
|
|
394
542
|
/**
|
|
@@ -398,6 +546,20 @@ function main() {
|
|
|
398
546
|
console.log('š Generating Component Usage Report...\n');
|
|
399
547
|
|
|
400
548
|
try {
|
|
549
|
+
// Check if running in the library repo itself
|
|
550
|
+
if (isLibraryRepo()) {
|
|
551
|
+
const report = generateSampleReport();
|
|
552
|
+
const outputPath = path.join(projectRoot, 'SAMPLE_' + outputFile);
|
|
553
|
+
fs.writeFileSync(outputPath, report, 'utf8');
|
|
554
|
+
|
|
555
|
+
console.log(`\nā
Sample usage report generated successfully!`);
|
|
556
|
+
console.log(`š Output: ${outputPath}`);
|
|
557
|
+
console.log(`š File size: ${(Buffer.byteLength(report, 'utf8') / 1024).toFixed(2)} KB`);
|
|
558
|
+
console.log(`\nš” This sample report shows component usage in Storybook files.`);
|
|
559
|
+
console.log(` To see real usage, run this command in a project that depends on this library.`);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
401
563
|
// Check if package is installed
|
|
402
564
|
const packagePath = path.join(projectRoot, 'node_modules', PACKAGE_NAME);
|
|
403
565
|
if (!fs.existsSync(packagePath)) {
|