@@ -10,7 +10,7 @@ const changes = {
1010 removed : { } , // Group by path
1111 modified : { } , // Group by path
1212 components : new Set ( ) , // Track changed components
13- affectedByComponents : { } // Track paths affected by component changes
13+ affectedByComponents : new Map ( ) // Track path/method combinations affected by component changes
1414} ;
1515
1616// Helper function to track component references
@@ -61,22 +61,21 @@ function findAffectedPaths() {
6161 if ( changes . components . size === 0 ) return ;
6262
6363 Object . entries ( currentSpec . paths || { } ) . forEach ( ( [ path , methods ] ) => {
64- const affectedMethods = [ ] ;
6564 Object . entries ( methods ) . forEach ( ( [ method , details ] ) => {
6665 const usedComponents = new Set ( ) ;
6766 findComponentRefs ( details , usedComponents ) ;
6867
6968 for ( const comp of usedComponents ) {
7069 if ( changes . components . has ( comp ) ) {
71- affectedMethods . push ( method . toUpperCase ( ) ) ;
72- if ( ! changes . affectedByComponents [ path ] ) {
73- changes . affectedByComponents [ path ] = {
74- methods : new Set ( ) ,
70+ const key = `${ path } ::${ method . toUpperCase ( ) } ` ;
71+ if ( ! changes . affectedByComponents . has ( key ) ) {
72+ changes . affectedByComponents . set ( key , {
73+ path,
74+ method : method . toUpperCase ( ) ,
7575 components : new Set ( )
76- } ;
76+ } ) ;
7777 }
78- changes . affectedByComponents [ path ] . methods . add ( method . toUpperCase ( ) ) ;
79- changes . affectedByComponents [ path ] . components . add ( comp ) ;
78+ changes . affectedByComponents . get ( key ) . components . add ( comp ) ;
8079 }
8180 }
8281 } ) ;
@@ -241,9 +240,6 @@ function findComponentUsage(details, componentName) {
241240
242241// Generate markdown release notes
243242function generateReleaseNotes ( ) {
244- let releaseDescription = '' ;
245-
246-
247243 const sections = [ ] ;
248244
249245 // Added endpoints
@@ -257,84 +253,99 @@ function generateReleaseNotes() {
257253 sections . push ( section ) ;
258254 }
259255
260- // Helper function to generate route modification details
261- function generateModifiedRouteDetails ( path , changes ) {
262- let details = '' ;
263- const methodsToProcess = new Set ( ) ;
264-
265- // Collect all affected methods
266- if ( changes . modified [ path ] ) {
267- changes . modified [ path ] . forEach ( ( { method} ) => methodsToProcess . add ( method ) ) ;
268- }
269- if ( changes . affectedByComponents [ path ] ) {
270- changes . affectedByComponents [ path ] . methods . forEach ( method => methodsToProcess . add ( method ) ) ;
271- }
256+ // Modified endpoints
257+ if ( Object . keys ( changes . modified ) . length > 0 || changes . affectedByComponents . size > 0 ) {
258+ let section = '## Modified\n' ;
272259
273- // Process each method
274- Array . from ( methodsToProcess )
275- . sort ( )
276- . forEach ( method => {
277- details += `- [${ method } ] \`${ path } \`\n` ;
278-
279- // Add direct changes
280- const directChanges = changes . modified [ path ] ?. find ( m => m . method === method ) ;
281- if ( directChanges ) {
282- directChanges . changes . sort ( ) . forEach ( change => {
283- details += ` - ${ change } \n` ;
284- } ) ;
285- }
260+ // Group affected paths
261+ const affectedPaths = new Map ( ) ;
286262
287- // Add component changes
288- if ( changes . affectedByComponents [ path ] ?. methods . has ( method ) ) {
289- const methodDetails = currentSpec . paths [ path ] [ method . toLowerCase ( ) ] ;
290- Array . from ( changes . affectedByComponents [ path ] . components )
291- . sort ( )
292- . forEach ( component => {
293- const usageLocations = findComponentUsage ( methodDetails , component ) . sort ( ) ;
294- details += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
295- } ) ;
263+ // Add directly modified paths
264+ Object . entries ( changes . modified ) . forEach ( ( [ path , methodChanges ] ) => {
265+ methodChanges . forEach ( ( { method, changes : methodChanges } ) => {
266+ if ( ! affectedPaths . has ( path ) ) {
267+ affectedPaths . set ( path , new Map ( ) ) ;
296268 }
269+ affectedPaths . get ( path ) . set ( method , { direct : methodChanges } ) ;
297270 } ) ;
298- return details ;
299- }
300-
301- // Modified endpoints
302- if ( Object . keys ( changes . modified ) . length > 0 || Object . keys ( changes . affectedByComponents ) . length > 0 ) {
303- let section = '## Modified\n' ;
304-
305- // First show all directly modified paths
306- const directlyModifiedPaths = Object . keys ( changes . modified ) . sort ( ) ;
307- directlyModifiedPaths . forEach ( path => {
308- section += generateModifiedRouteDetails ( path , changes ) ;
309271 } ) ;
310272
311- // Then show component-affected paths (but not ones that were directly modified)
312- const componentAffectedEntries = Object . entries ( changes . affectedByComponents )
313- . filter ( ( [ path ] ) => ! changes . modified [ path ] ) // Only paths not already shown above
314- . flatMap ( ( [ path , details ] ) =>
315- Array . from ( details . methods ) . map ( method => ( { path, method} ) )
316- )
317- . sort ( ( a , b ) => a . path . localeCompare ( b . path ) || a . method . localeCompare ( b . method ) ) ;
273+ // Add component-affected paths
274+ for ( const [ _ , value ] of changes . affectedByComponents ) {
275+ const { path, method, components } = value ;
276+ if ( ! affectedPaths . has ( path ) ) {
277+ affectedPaths . set ( path , new Map ( ) ) ;
278+ }
279+ const existing = affectedPaths . get ( path ) . get ( method ) || { } ;
280+ affectedPaths . get ( path ) . set ( method , {
281+ ...existing ,
282+ components : Array . from ( components )
283+ } ) ;
284+ }
318285
319- // Show first 5 component-affected method-path combinations
320- const visibleEntries = componentAffectedEntries . slice ( 0 , 5 ) ;
321- const processedPaths = new Set ( ) ;
286+ // Sort paths and generate output
287+ const sortedPaths = Array . from ( affectedPaths . keys ( ) ) . sort ( ) ;
288+ const visiblePaths = sortedPaths . slice ( 0 , 5 ) ;
322289
323- visibleEntries . forEach ( ( { path} ) => {
324- if ( ! processedPaths . has ( path ) ) {
325- section += generateModifiedRouteDetails ( path , changes ) ;
326- processedPaths . add ( path ) ;
327- }
290+ // Show first 5 paths
291+ visiblePaths . forEach ( path => {
292+ const methods = affectedPaths . get ( path ) ;
293+ Array . from ( methods . entries ( ) )
294+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
295+ . forEach ( ( [ method , info ] ) => {
296+ section += `- [${ method } ] \`${ path } \`\n` ;
297+
298+ // Show direct changes
299+ if ( info . direct ) {
300+ info . direct . sort ( ) . forEach ( change => {
301+ section += ` - ${ change } \n` ;
302+ } ) ;
303+ }
304+
305+ // Show component changes
306+ if ( info . components ) {
307+ const methodDetails = currentSpec . paths [ path ] [ method . toLowerCase ( ) ] ;
308+ info . components
309+ . sort ( )
310+ . forEach ( component => {
311+ const usageLocations = findComponentUsage ( methodDetails , component ) . sort ( ) ;
312+ if ( usageLocations . length > 0 ) {
313+ section += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
314+ }
315+ } ) ;
316+ }
317+ } ) ;
328318 } ) ;
329319
330- // Collapse any remaining entries
331- const remainingEntries = componentAffectedEntries . slice ( 5 ) ;
332- if ( remainingEntries . length > 0 ) {
320+ // Collapse remaining paths
321+ const remainingPaths = sortedPaths . slice ( 5 ) ;
322+ if ( remainingPaths . length > 0 ) {
333323 section += '\n<details><summary>Show more routes affected by component changes...</summary>\n\n' ;
334- const remainingPaths = new Set ( ) ;
335- remainingEntries . forEach ( ( { path} ) => remainingPaths . add ( path ) ) ;
336- Array . from ( remainingPaths ) . sort ( ) . forEach ( path => {
337- section += generateModifiedRouteDetails ( path , changes ) ;
324+ remainingPaths . forEach ( path => {
325+ const methods = affectedPaths . get ( path ) ;
326+ Array . from ( methods . entries ( ) )
327+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
328+ . forEach ( ( [ method , info ] ) => {
329+ section += `- [${ method } ] \`${ path } \`\n` ;
330+
331+ if ( info . direct ) {
332+ info . direct . sort ( ) . forEach ( change => {
333+ section += ` - ${ change } \n` ;
334+ } ) ;
335+ }
336+
337+ if ( info . components ) {
338+ const methodDetails = currentSpec . paths [ path ] [ method . toLowerCase ( ) ] ;
339+ info . components
340+ . sort ( )
341+ . forEach ( component => {
342+ const usageLocations = findComponentUsage ( methodDetails , component ) . sort ( ) ;
343+ if ( usageLocations . length > 0 ) {
344+ section += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
345+ }
346+ } ) ;
347+ }
348+ } ) ;
338349 } ) ;
339350 section += '</details>\n' ;
340351 }
@@ -353,17 +364,14 @@ function generateReleaseNotes() {
353364 sections . push ( section ) ;
354365 }
355366
356-
357367 // Sort sections alphabetically and combine
358368 sections . sort ( ( a , b ) => {
359369 const titleA = a . split ( '\n' ) [ 0 ] ;
360370 const titleB = b . split ( '\n' ) [ 0 ] ;
361371 return titleA . localeCompare ( titleB ) ;
362372 } ) ;
363373
364- releaseDescription += sections . join ( '\n' ) ;
365-
366- return releaseDescription ;
374+ return sections . join ( '\n' ) ;
367375}
368376
369377// Main execution
0 commit comments