import * as diff from 'diff' ; const jsDiff = diff ; export enum DiffType { DEFAULT = 0 , ADDED = 1 , REMOVED = 2 } export enum DiffMethod { CHARS = 'diffChars' , WORDS = 'diffWords' , WORDS_WITH_SPACE = 'diffWordsWithSpace' , LINES = 'diffLines' , TRIMMED_LINES = 'diffTrimmedLines' , SENTENCES = 'diffSentences' , CSS = 'diffCss' } export interface DiffInformation { value ? : string | DiffInformation [ ] ; lineNumber ? : number ; type ? : DiffType ; } export interface LineInformation { left ? : DiffInformation ; right ? : DiffInformation ; } export interface ComputedLineInformation { lineInformation : LineInformation [ ] ; diffLines : number [ ] ; } export interface ComputedDiffInformation { left ? : DiffInformation [ ] ; right ? : DiffInformation [ ] ; } export interface JsDiffChangeObject { added ? : boolean ; removed ? : boolean ; value ? : string ; } param value const constructLines = ( value ) : string [ ] => { const lines = value . split ( '\n' ) ; const isAllEmpty = lines . every ( ( val ) : boolean => ! val ) ; if ( isAllEmpty ) { if ( lines . length === 2 ) { return [ ] ; } lines . pop ( ) ; return lines ; } const lastLine = lines [ lines . length - 1 ] ; const firstLine = lines [ 0 ] ; if ( ! lastLine ) { lines . pop ( ) ; } if ( ! firstLine ) { lines . shift ( ) ; } return lines ; } ; param oldValue param newValue param compareMethod const computeDiff = ( oldValue , newValue , compareMethod = DiffMethod . CHARS ) : ComputedDiffInformation => { const diffArray = jsDiff [ compareMethod ] ( oldValue , newValue ) ; const computedDiff = { left : [ ] , right : [ ] , } ; diffArray . forEach ( ( { added , removed , value } ) : DiffInformation => { const diffInformation = { } ; if ( added ) { diffInformation . type = DiffType . ADDED ; diffInformation . value = value ; computedDiff . right . push ( diffInformation ) ; } if ( removed ) { diffInformation . type = DiffType . REMOVED ; diffInformation . value = value ; computedDiff . left . push ( diffInformation ) ; } if ( ! removed && ! added ) { diffInformation . type = DiffType . DEFAULT ; diffInformation . value = value ; computedDiff . right . push ( diffInformation ) ; computedDiff . left . push ( diffInformation ) ; } return diffInformation ; } ) ; return computedDiff ; } ; param oldString param newString param disableWordDiff param compareMethod param linesOffset const computeLineInformation = ( oldString , newString , disableWordDiff = false , compareMethod = DiffMethod . CHARS , linesOffset = 0 ) : ComputedLineInformation => { const diffArray = diff . diffLines ( oldString . trimRight ( ) , newString . trimRight ( ) , { newlineIsToken : true , ignoreWhitespace : false , ignoreCase : false , } ) ; let rightLineNumber = linesOffset ; let leftLineNumber = linesOffset ; let lineInformation = [ ] ; let counter = 0 ; const diffLines = [ ] ; const ignoreDiffIndexes = [ ] ; const getLineInformation = ( value , diffIndex , added ? , removed ? , evaluateOnlyFirstLine ? ) : LineInformation [ ] => { const lines = constructLines ( value ) ; return lines . map ( ( line , lineIndex ) : LineInformation => { const left = { } ; const right = { } ; if ( ignoreDiffIndexes . includes ( `${ diffIndex }-${ lineIndex }` ) || ( evaluateOnlyFirstLine && lineIndex !== 0 ) ) { return undefined ; } if ( added || removed ) { if ( ! diffLines . includes ( counter ) ) { diffLines . push ( counter ) ; } if ( removed ) { leftLineNumber += 1 ; left . lineNumber = leftLineNumber ; left . type = DiffType . REMOVED ; left . value = line || ' ' ; const nextDiff = diffArray [ diffIndex + 1 ] ; if ( nextDiff && nextDiff . added ) { const nextDiffLines = constructLines ( nextDiff . value ) [ lineIndex ] ; if ( nextDiffLines ) { const { value : rightValue , lineNumber , type , } = getLineInformation ( nextDiff . value , diffIndex , true , false , true ) [ 0 ] . right ; ignoreDiffIndexes . push ( `${ diffIndex + 1 }-${ lineIndex }` ) ; right . lineNumber = lineNumber ; right . type = type ; if ( disableWordDiff ) { right . value = rightValue ; } else { const computedDiff = computeDiff ( line , rightValue as string , compareMethod ) ; right . value = computedDiff . right ; left . value = computedDiff . left ; } } } } else { rightLineNumber += 1 ; right . lineNumber = rightLineNumber ; right . type = DiffType . ADDED ; right . value = line ; } } else { leftLineNumber += 1 ; rightLineNumber += 1 ; left . lineNumber = leftLineNumber ; left . type = DiffType . DEFAULT ; left . value = line ; right . lineNumber = rightLineNumber ; right . type = DiffType . DEFAULT ; right . value = line ; } counter += 1 ; return { right , left } ; } ) . filter ( Boolean ) ; } ; diffArray . forEach ( ( { added , removed , value } , index ) : void => { lineInformation = [ ... lineInformation , ... getLineInformation ( value , index , added , removed ) , ] ; } ) ; return { lineInformation , diffLines , } ; } ; export { computeLineInformation } ;