@@ -994,6 +994,37 @@ describe('trace mode (deferred violations)', () => {
994994
995995 errorSpy . mockRestore ( )
996996 } )
997+
998+ it ( 'deduplicates deferred violations using shared warnedMessages set from matcher' , async ( ) => {
999+ const errorSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } )
1000+
1001+ // Use two patterns that produce the same message for the same import
1002+ const plugins = ImpoundPlugin . rollup ( { trace : true , patterns : [ [ 'secret' , 'Not allowed' ] ] , error : false } )
1003+ const pluginArray = Array . isArray ( plugins ) ? plugins : [ plugins ]
1004+ const impoundPlugin = pluginArray . find ( p => p . name === 'impound' ) !
1005+ const tracePlugin = pluginArray . find ( p => p . name === 'impound:trace' ) !
1006+
1007+ const context = { error : ( ) => { } }
1008+
1009+ // First: defer a violation, then flush via transform
1010+ await ( impoundPlugin as any ) . resolveId . call ( context , 'secret' , 'a.js' )
1011+ await ( tracePlugin as any ) . transform ( 'import secret from "secret"' , 'a.js' )
1012+ expect ( errorSpy ) . toHaveBeenCalledTimes ( 1 )
1013+
1014+ // Second: same import from a different file triggers immediate path (a.js already in graph).
1015+ // The warnedMessages set is shared with the matcher, so the *immediate* violation from b.js
1016+ // is a different message (different importer) and should log.
1017+ await ( tracePlugin as any ) . transform ( 'import secret from "secret"' , 'b.js' )
1018+ await ( impoundPlugin as any ) . resolveId . call ( context , 'secret' , 'b.js' )
1019+ expect ( errorSpy ) . toHaveBeenCalledTimes ( 2 )
1020+
1021+ // Third: re-resolve 'secret' from a.js — immediate path, same message as first.
1022+ // Should be deduped because the deferred flush used the matcher's warnedMessages set.
1023+ await ( impoundPlugin as any ) . resolveId . call ( context , 'secret' , 'a.js' )
1024+ expect ( errorSpy ) . toHaveBeenCalledTimes ( 2 ) // still 2, deduped
1025+
1026+ errorSpy . mockRestore ( )
1027+ } )
9971028} )
9981029
9991030async function buildWithTrace ( files : Record < string , string > , libs : string [ ] , opts : ImpoundOptions , extraPlugins : any [ ] = [ ] ) {
0 commit comments