|
16 | 16 | /* eslint-disable no-console */
|
17 | 17 |
|
18 | 18 | /**
|
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. |
21 | 22 | *
|
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: |
23 | 31 | * node tasks/migrated-component-scanner.js [--output=path/to/output.json]
|
24 | 32 | */
|
25 | 33 |
|
@@ -123,43 +131,84 @@ function generateUrlFragmentFromTitle(title) {
|
123 | 131 | }
|
124 | 132 |
|
125 | 133 | /**
|
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. |
128 | 144 | */
|
129 | 145 | function generateMigratedComponentsReport() {
|
130 | 146 | const allComponents = getAllComponentDirectories();
|
131 | 147 | const migratedComponents = getComponentsByStatus("migrated");
|
132 | 148 |
|
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) => { |
134 | 152 | const storiesDir = path.resolve(process.cwd(), "components", dir, "stories");
|
135 | 153 | 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 [{ |
137 | 156 | name: dir,
|
138 | 157 | title: dir,
|
139 | 158 | url: dir,
|
140 |
| - }; |
| 159 | + }]; |
141 | 160 | }
|
142 | 161 |
|
143 | 162 | const storyFiles = fs.readdirSync(storiesDir).filter(file => file.endsWith(".stories.js"));
|
144 |
| - let title = null; |
| 163 | + |
| 164 | + const entries = []; |
145 | 165 | 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 | + }); |
148 | 185 | }
|
149 | 186 |
|
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 | + } |
151 | 197 |
|
152 |
| - return { |
153 |
| - name: dir, |
154 |
| - title: title || dir, |
155 |
| - url: urlFragment, |
156 |
| - }; |
| 198 | + return entries; |
157 | 199 | });
|
158 | 200 |
|
| 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 | + |
159 | 208 | return {
|
160 | 209 | total: allComponents.length,
|
161 |
| - migrated: migratedComponents.length, |
162 |
| - components: componentsWithData, |
| 210 | + migrated: migratedComponentData.length, |
| 211 | + components: componentsSorted, |
163 | 212 | generatedAt: new Date().toISOString()
|
164 | 213 | };
|
165 | 214 | }
|
@@ -192,7 +241,10 @@ if (require.main === module) {
|
192 | 241 | console.log(`Found ${report.migrated} migrated components out of ${report.total} total components.`);
|
193 | 242 | } else {
|
194 | 243 | 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(", ")); |
196 | 248 | console.log(`\nTotal: ${report.migrated} out of ${report.total} components are migrated.`);
|
197 | 249 | }
|
198 | 250 | })();
|
|
0 commit comments