Skip to content

Commit 62d0b78

Browse files
chore: pr feedback
- alphabetically sort console output - handle directories that have multiple story files with different components/titles - add code comments/documentation
1 parent 895af59 commit 62d0b78

File tree

1 file changed

+72
-20
lines changed

1 file changed

+72
-20
lines changed

tasks/migrated-component-scanner.js

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@
1616
/* eslint-disable no-console */
1717

1818
/**
19-
* This script scans the components directory and generates a list of components
20-
* with a migrated status. The output can be saved to a JSON file or printed to console.
19+
* this script scans the `components` directory and builds a list of storybook
20+
* components that are marked as migrated. it can either print a human-friendly
21+
* list to the console or write a structured json report to disk.
2122
*
22-
* Usage:
23+
* important behavior:
24+
* - a single component directory can contain multiple story files (for example,
25+
* text field and text area). when multiple story files are marked as migrated,
26+
* this script returns one entry per story so each item appears in the output.
27+
* - console output is alphabetically sorted by title (case- and accent-insensitive)
28+
* to make it easier to scan.
29+
*
30+
* usage:
2331
* node tasks/migrated-component-scanner.js [--output=path/to/output.json]
2432
*/
2533

@@ -123,43 +131,84 @@ function generateUrlFragmentFromTitle(title) {
123131
}
124132

125133
/**
126-
* Generates a list of migrated components with their titles, and docs links
127-
* @returns {Object} Information about migrated components
134+
* generate a list of migrated components with titles and doc links.
135+
*
136+
* notes on shape of data:
137+
* - the returned `components` array can include multiple entries for a single
138+
* component directory when there are multiple migrated story files present.
139+
* - each entry includes the human-friendly title and a url fragment derived
140+
* from that title for linking.
141+
*
142+
* @returns {{ total: number, migrated: number, components: Array<{name: string, title: string, url: string}>, generatedAt: string }}
143+
* information about migrated components and counts.
128144
*/
129145
function generateMigratedComponentsReport() {
130146
const allComponents = getAllComponentDirectories();
131147
const migratedComponents = getComponentsByStatus("migrated");
132148

133-
const componentsWithData = migratedComponents.map((dir) => {
149+
// build entries per component directory depending on how many story files
150+
// inside the directory are marked as `type: "migrated"`.
151+
const migratedComponentData = migratedComponents.flatMap((dir) => {
134152
const storiesDir = path.resolve(process.cwd(), "components", dir, "stories");
135153
if (!fs.existsSync(storiesDir)) {
136-
return {
154+
// no stories directory; fall back to a single, generic entry so the component still shows up in the report.
155+
return [{
137156
name: dir,
138157
title: dir,
139158
url: dir,
140-
};
159+
}];
141160
}
142161

143162
const storyFiles = fs.readdirSync(storiesDir).filter(file => file.endsWith(".stories.js"));
144-
let title = null;
163+
164+
const entries = [];
145165
for (const file of storyFiles) {
146-
title = extractTitleFromStoryFile(path.join(storiesDir, file));
147-
if (title) break;
166+
const filePath = path.join(storiesDir, file);
167+
let content = "";
168+
try {
169+
content = fs.readFileSync(filePath, "utf8");
170+
} catch (_) {
171+
// if a file cannot be read, skip it and continue with others.
172+
continue;
173+
}
174+
175+
// only include story files that declare `type: "migrated"` in their metadata.
176+
if (!content.includes("type: \"migrated\"")) continue;
177+
178+
const title = extractTitleFromStoryFile(filePath) || dir;
179+
const urlFragment = generateUrlFragmentFromTitle(title);
180+
entries.push({
181+
name: dir,
182+
title,
183+
url: urlFragment,
184+
});
148185
}
149186

150-
const urlFragment = generateUrlFragmentFromTitle(title || dir);
187+
// if there are stories but none are marked as migrated, include a single
188+
// fallback entry for the directory to keep counts consistent.
189+
if (entries.length === 0) {
190+
const urlFragment = generateUrlFragmentFromTitle(dir);
191+
return [{
192+
name: dir,
193+
title: dir,
194+
url: urlFragment,
195+
}];
196+
}
151197

152-
return {
153-
name: dir,
154-
title: title || dir,
155-
url: urlFragment,
156-
};
198+
return entries;
157199
});
158200

201+
// sort the final array alphabetically by title (fallback to name),
202+
// keeping the full objects intact for json output and rendering.
203+
const componentsSorted = migratedComponentData
204+
.slice()
205+
.sort((a, b) => (a.title || a.name)
206+
.localeCompare(b.title || b.name, undefined, { sensitivity: "base" }));
207+
159208
return {
160209
total: allComponents.length,
161-
migrated: migratedComponents.length,
162-
components: componentsWithData,
210+
migrated: migratedComponentData.length,
211+
components: componentsSorted,
163212
generatedAt: new Date().toISOString()
164213
};
165214
}
@@ -192,7 +241,10 @@ if (require.main === module) {
192241
console.log(`Found ${report.migrated} migrated components out of ${report.total} total components.`);
193242
} else {
194243
console.log("Migrated Components:");
195-
console.log(report.components.join(", "));
244+
const componentNames = report.components
245+
.map(component => component.title || component.name || component)
246+
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
247+
console.log(componentNames.join(", "));
196248
console.log(`\nTotal: ${report.migrated} out of ${report.total} components are migrated.`);
197249
}
198250
})();

0 commit comments

Comments
 (0)