@@ -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 } ) ;
@@ -244,9 +243,6 @@ function findComponentUsage(details, componentName) {
244243
245244// Generate markdown release notes
246245function generateReleaseNotes ( ) {
247- let releaseDescription = '' ;
248-
249-
250246 const sections = [ ] ;
251247
252248 // Added endpoints
@@ -260,84 +256,85 @@ function generateReleaseNotes() {
260256 sections . push ( section ) ;
261257 }
262258
263- // Helper function to generate route modification details
264- function generateModifiedRouteDetails ( path , changes ) {
265- let details = '' ;
266- const methodsToProcess = new Set ( ) ;
259+ // Modified endpoints
260+ if ( Object . keys ( changes . modified ) . length > 0 || changes . affectedByComponents . size > 0 ) {
261+ let section = '## Modified\n' ;
262+
263+ // First show all directly modified paths
264+ Object . entries ( changes . modified )
265+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
266+ . forEach ( ( [ path , methodChanges ] ) => {
267+ methodChanges
268+ . sort ( ( a , b ) => a . method . localeCompare ( b . method ) )
269+ . forEach ( ( { method, changes : methodChanges } ) => {
270+ section += `- [${ method } ] \`${ path } \`\n` ;
271+ methodChanges . sort ( ) . forEach ( change => {
272+ section += ` - ${ change } \n` ;
273+ } ) ;
274+ } ) ;
275+ } ) ;
276+
277+ // Then handle component-affected paths
278+ const componentAffectedPaths = new Map ( ) ;
267279
268- // Collect all affected methods
269- if ( changes . modified [ path ] ) {
270- changes . modified [ path ] . forEach ( ( { method} ) => methodsToProcess . add ( method ) ) ;
271- }
272- if ( changes . affectedByComponents [ path ] ) {
273- changes . affectedByComponents [ path ] . methods . forEach ( method => methodsToProcess . add ( method ) ) ;
280+ for ( const [ _ , value ] of changes . affectedByComponents ) {
281+ const { path, method, components } = value ;
282+ // Skip if this path/method was already shown in direct modifications
283+ if ( changes . modified [ path ] ?. some ( m => m . method === method ) ) continue ;
284+
285+ if ( ! componentAffectedPaths . has ( path ) ) {
286+ componentAffectedPaths . set ( path , new Map ( ) ) ;
287+ }
288+ componentAffectedPaths . get ( path ) . set ( method , Array . from ( components ) ) ;
274289 }
275290
276- // Process each method
277- Array . from ( methodsToProcess )
278- . sort ( )
279- . forEach ( method => {
280- details += `- [${ method } ] \`${ path } \`\n` ;
281-
282- // Add direct changes
283- const directChanges = changes . modified [ path ] ?. find ( m => m . method === method ) ;
284- if ( directChanges ) {
285- directChanges . changes . sort ( ) . forEach ( change => {
286- details += ` - ${ change } \n` ;
287- } ) ;
288- }
291+ // Show first 5 component-affected paths
292+ const sortedComponentPaths = Array . from ( componentAffectedPaths . keys ( ) ) . sort ( ) ;
293+ const visibleComponentPaths = sortedComponentPaths . slice ( 0 , 5 ) ;
294+
295+ // Add a blank line before component-affected paths if there were direct modifications
296+ if ( Object . keys ( changes . modified ) . length > 0 && visibleComponentPaths . length > 0 ) {
297+ section += '\n' ;
298+ }
289299
290- // Add component changes
291- if ( changes . affectedByComponents [ path ] ?. methods . has ( method ) ) {
300+ visibleComponentPaths . forEach ( path => {
301+ const methods = componentAffectedPaths . get ( path ) ;
302+ Array . from ( methods . entries ( ) )
303+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
304+ . forEach ( ( [ method , components ] ) => {
305+ section += `- [${ method } ] \`${ path } \`\n` ;
292306 const methodDetails = currentSpec . paths [ path ] [ method . toLowerCase ( ) ] ;
293- Array . from ( changes . affectedByComponents [ path ] . components )
307+ components
294308 . sort ( )
295309 . forEach ( component => {
296310 const usageLocations = findComponentUsage ( methodDetails , component ) . sort ( ) ;
297- details += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
311+ if ( usageLocations . length > 0 ) {
312+ section += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
313+ }
298314 } ) ;
299- }
300- } ) ;
301- return details ;
302- }
303-
304- // Modified endpoints
305- if ( Object . keys ( changes . modified ) . length > 0 || Object . keys ( changes . affectedByComponents ) . length > 0 ) {
306- let section = '## Modified\n' ;
307-
308- // First show all directly modified paths
309- const directlyModifiedPaths = Object . keys ( changes . modified ) . sort ( ) ;
310- directlyModifiedPaths . forEach ( path => {
311- section += generateModifiedRouteDetails ( path , changes ) ;
312- } ) ;
313-
314- // Then show component-affected paths (but not ones that were directly modified)
315- const componentAffectedEntries = Object . entries ( changes . affectedByComponents )
316- . filter ( ( [ path ] ) => ! changes . modified [ path ] ) // Only paths not already shown above
317- . flatMap ( ( [ path , details ] ) =>
318- Array . from ( details . methods ) . map ( method => ( { path, method} ) )
319- )
320- . sort ( ( a , b ) => a . path . localeCompare ( b . path ) || a . method . localeCompare ( b . method ) ) ;
321-
322- // Show first 5 component-affected method-path combinations
323- const visibleEntries = componentAffectedEntries . slice ( 0 , 5 ) ;
324- const processedPaths = new Set ( ) ;
325-
326- visibleEntries . forEach ( ( { path} ) => {
327- if ( ! processedPaths . has ( path ) ) {
328- section += generateModifiedRouteDetails ( path , changes ) ;
329- processedPaths . add ( path ) ;
330- }
315+ } ) ;
331316 } ) ;
332317
333- // Collapse any remaining entries
334- const remainingEntries = componentAffectedEntries . slice ( 5 ) ;
335- if ( remainingEntries . length > 0 ) {
318+ // Collapse remaining component-affected paths
319+ const remainingPaths = sortedComponentPaths . slice ( 5 ) ;
320+ if ( remainingPaths . length > 0 ) {
336321 section += '\n<details><summary>Show more routes affected by component changes...</summary>\n\n' ;
337- const remainingPaths = new Set ( ) ;
338- remainingEntries . forEach ( ( { path} ) => remainingPaths . add ( path ) ) ;
339- Array . from ( remainingPaths ) . sort ( ) . forEach ( path => {
340- section += generateModifiedRouteDetails ( path , changes ) ;
322+ remainingPaths . forEach ( path => {
323+ const methods = componentAffectedPaths . get ( path ) ;
324+ Array . from ( methods . entries ( ) )
325+ . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
326+ . forEach ( ( [ method , components ] ) => {
327+ section += `- [${ method } ] \`${ path } \`\n` ;
328+ const methodDetails = currentSpec . paths [ path ] [ method . toLowerCase ( ) ] ;
329+ components
330+ . sort ( )
331+ . forEach ( component => {
332+ const usageLocations = findComponentUsage ( methodDetails , component ) . sort ( ) ;
333+ if ( usageLocations . length > 0 ) {
334+ section += ` - \`${ component } \` modified in ${ usageLocations . join ( ', ' ) } \n` ;
335+ }
336+ } ) ;
337+ } ) ;
341338 } ) ;
342339 section += '</details>\n' ;
343340 }
@@ -356,17 +353,14 @@ function generateReleaseNotes() {
356353 sections . push ( section ) ;
357354 }
358355
359-
360356 // Sort sections alphabetically and combine
361357 sections . sort ( ( a , b ) => {
362358 const titleA = a . split ( '\n' ) [ 0 ] ;
363359 const titleB = b . split ( '\n' ) [ 0 ] ;
364360 return titleA . localeCompare ( titleB ) ;
365361 } ) ;
366362
367- releaseDescription += sections . join ( '\n' ) ;
368-
369- return releaseDescription ;
363+ return sections . join ( '\n' ) ;
370364}
371365
372366// Main execution
0 commit comments