@@ -43,7 +43,7 @@ function extractJumpLinesFromPretty(prettyLines?: string[]): Set<number> {
4343export function spectralDiagnosticsToIssuesSimple (
4444 diagnostics : SpectralCore . ISpectralDiagnostic [ ] | readonly SpectralCore . ISpectralDiagnostic [ ] | undefined ,
4545 prettyLines ?: string [ ] ,
46- fallbackAddOne = false // om ingen referens finns, välj detta
46+ fallbackAddOne = false // If no ref, choose thisone
4747) : Issue [ ] {
4848 const issues : Issue [ ] = [ ] ;
4949 if ( ! diagnostics || diagnostics . length === 0 ) return issues ;
@@ -59,18 +59,18 @@ export function spectralDiagnosticsToIssuesSimple(
5959 let line : number | null = null ;
6060 if ( rawLine !== null ) {
6161 if ( useJumpRef ) {
62- // Om prettifier har rawLine+1 så använd +1, annars använd rawLine (ingen annan gissning)
62+ // Om prettifier har rawLine+1 så använd +1, annars använd rawLine
6363 if ( jumpSet . has ( rawLine + 1 ) ) {
6464 line = rawLine + 1 ;
6565 } else {
6666 line = rawLine ;
6767 }
6868 } else {
69- // ingen referens — fallback baserat på param
69+ // No ref — fallback
7070 line = fallbackAddOne ? rawLine + 1 : rawLine ;
7171 }
7272 }
73-
73+ //Create Issue
7474 issues . push ( {
7575 type : 'Semantic' ,
7676 code : d . code ?? undefined ,
@@ -207,12 +207,113 @@ export function mergeAndDedupeIssues(prettyIssues: Issue[], spectralIssues: Issu
207207
208208 return result ;
209209}
210+ export function consolidateIssues ( issues : Issue [ ] ) : Issue [ ] {
211+ if ( ! issues || issues . length === 0 ) return [ ] ;
212+
213+ const normalizeMsg = ( s ?: string ) => ( s ?? '' ) . replace ( / \s + / g, ' ' ) . trim ( ) . toLowerCase ( ) ;
214+ const normalizePath = ( p ?: string ) => ( p ?? '' ) . toString ( ) . trim ( ) ;
215+
216+ // Key now: path + line (group by location), not path+message
217+ const map = new Map < string , Issue > ( ) ;
218+
219+ const priorityScore = ( type ?: string ) => {
220+ if ( ! type ) return 0 ;
221+ const t = type . toLowerCase ( ) ;
222+ if ( t === 'semantic' ) return 3 ;
223+ if ( t === 'structural' ) return 2 ;
224+ if ( t === 'info' ) return 1 ;
225+ return 0 ;
226+ } ;
227+
228+ for ( const rawIss of issues ) {
229+ const iss : Issue = { ...rawIss } ; // shallow copy
230+ iss . path = normalizePath ( iss . path ) ;
231+ iss . message = ( iss . message ?? '' ) . trim ( ) ;
232+
233+ const linePart = iss . line != null ? String ( iss . line ) : '_noline_' ;
234+ const key = `${ iss . path } |${ linePart } ` ;
235+
236+ const existing = map . get ( key ) ;
237+ if ( ! existing ) {
238+ // ensure details is array
239+ if ( ! Array . isArray ( iss . details ) ) iss . details = [ ] ;
240+ if ( iss . raw && ! Array . isArray ( iss . raw ) ) iss . raw = [ String ( iss . raw ) ] ;
241+ map . set ( key , iss ) ;
242+ continue ;
243+ }
244+
245+ // If incoming has higher priority (e.g. Semantic > Structural) - replace as primary
246+ if ( priorityScore ( iss . type ) > priorityScore ( existing . type ) ) {
247+ // preserve existing as detail(s)
248+ const details = new Set < string > ( existing . details ?? [ ] ) ;
249+ if ( existing . message && existing . message !== iss . message ) details . add ( existing . message ) ;
250+ if ( existing . raw && Array . isArray ( existing . raw ) ) {
251+ for ( const r of existing . raw ) details . add ( String ( r ) . trim ( ) ) ;
252+ }
253+ // merge existing.details too
254+ if ( Array . isArray ( existing . details ) ) {
255+ for ( const d of existing . details ) details . add ( ( d ?? '' ) . toString ( ) . trim ( ) ) ;
256+ }
257+
258+ iss . details = Array . from ( new Set ( [ ...( iss . details ?? [ ] ) , ...Array . from ( details ) ] ) ) . filter ( Boolean ) ;
259+ // copy metadata if missing on new
260+ if ( ! iss . documentationUrl ) iss . documentationUrl = existing . documentationUrl ;
261+ if ( ! iss . source ) iss . source = existing . source ;
262+ // keep smallest non-null line if new misses it
263+ if ( ( iss . line == null || iss . line === 0 ) && ( existing . line != null ) ) iss . line = existing . line ;
264+ map . set ( key , iss ) ;
265+ } else {
266+ // Keep existing primary; merge incoming into details
267+ const details = new Set < string > ( existing . details ?? [ ] ) ;
268+ if ( iss . message && iss . message !== existing . message ) details . add ( iss . message ) ;
269+ if ( Array . isArray ( iss . details ) ) for ( const d of iss . details ) details . add ( d ) ;
270+ if ( Array . isArray ( iss . raw ) ) for ( const r of iss . raw ) details . add ( String ( r ) . trim ( ) ) ;
271+ existing . details = Array . from ( details ) . filter ( Boolean ) ;
272+ // keep smallest non-null line if existing missing
273+ if ( ( existing . line == null || existing . line === 0 ) && ( iss . line != null ) ) existing . line = iss . line ;
274+ existing . documentationUrl = existing . documentationUrl ?? iss . documentationUrl ;
275+ existing . source = existing . source ?? iss . source ;
276+ map . set ( key , existing ) ;
277+ }
278+ }
279+
280+ // Convert to array
281+ let merged = Array . from ( map . values ( ) ) ;
282+
283+ // Filter: ta bort generiska oneOf-meddelanden om specifika finns i samma parent path
284+ merged = merged . filter ( i => {
285+ if ( i . message && / o n e O f s c h e m a / i. test ( i . message ) ) {
286+ const parent = i . path . split ( '.' ) . slice ( 0 , - 1 ) . join ( '.' ) ;
287+ const hasSpecific = merged . some ( o => o . path . startsWith ( parent ) && ! / o n e O f s c h e m a / i. test ( o . message ) ) ;
288+ if ( hasSpecific ) return false ;
289+ }
290+ return true ;
291+ } ) ;
292+
293+ // Sort determenistic: source, line (nummer), path
294+ merged . sort ( ( a , b ) => {
295+ const sa = `${ a . source ?? '' } |${ String ( a . line ?? 0 ) . padStart ( 6 , '0' ) } |${ a . path ?? '' } ` ;
296+ const sb = `${ b . source ?? '' } |${ String ( b . line ?? 0 ) . padStart ( 6 , '0' ) } |${ b . path ?? '' } ` ;
297+ return sa . localeCompare ( sb ) ;
298+ } ) ;
299+
300+ // Trim messages & details, dedupe details
301+ for ( const i of merged ) {
302+ i . message = ( i . message ?? '' ) . trim ( ) ;
303+ if ( i . details && Array . isArray ( i . details ) ) {
304+ i . details = Array . from ( new Set ( i . details . map ( d => ( d ?? '' ) . toString ( ) . trim ( ) ) ) ) . filter ( Boolean ) ;
305+ }
306+ }
307+
308+ return merged ;
309+ }
310+
210311/**
211312 * ConsolidateIssues
212313 * @param issues
213314 * @returns
214315 */
215- export function consolidateIssues ( issues : Issue [ ] ) : Issue [ ] {
316+ export function consolidateIssues2 ( issues : Issue [ ] ) : Issue [ ] {
216317 if ( ! issues || issues . length === 0 ) return [ ] ;
217318
218319 // Normaliseringshjälpare
0 commit comments