index.js 4.51 KB
Newer Older
1
"use strict";module.exports=input=>{if(!input)return[];if(typeof input!=="string"||input.match(/^\s+$/))return[];const lines=input.split("\n");if(lines.length===0)return[];const files=[];let currentFile=null;let currentChunk=null;let deletedLineCounter=0;let addedLineCounter=0;let currentFileChanges=null;const normal=line=>{var _currentChunk;(_currentChunk=currentChunk)===null||_currentChunk===void 0?void 0:_currentChunk.changes.push({type:"normal",normal:true,ln1:deletedLineCounter++,ln2:addedLineCounter++,content:line});currentFileChanges.oldLines--;currentFileChanges.newLines--};const start=line=>{const[fromFileName,toFileName]=parseFiles(line)??[];currentFile={chunks:[],deletions:0,additions:0,from:fromFileName,to:toFileName};files.push(currentFile)};const restart=()=>{if(!currentFile||currentFile.chunks.length)start()};const newFile=()=>{restart();currentFile.new=true;currentFile.from="/dev/null"};const deletedFile=()=>{restart();currentFile.deleted=true;currentFile.to="/dev/null"};const index=line=>{restart();currentFile.index=line.split(" ").slice(1)};const fromFile=line=>{restart();currentFile.from=parseOldOrNewFile(line)};const toFile=line=>{restart();currentFile.to=parseOldOrNewFile(line)};const toNumOfLines=number=>+(number||1);const chunk=(line,match)=>{if(!currentFile)return;const[oldStart,oldNumLines,newStart,newNumLines]=match.slice(1);deletedLineCounter=+oldStart;addedLineCounter=+newStart;currentChunk={content:line,changes:[],oldStart:+oldStart,oldLines:toNumOfLines(oldNumLines),newStart:+newStart,newLines:toNumOfLines(newNumLines)};currentFileChanges={oldLines:toNumOfLines(oldNumLines),newLines:toNumOfLines(newNumLines)};currentFile.chunks.push(currentChunk)};const del=line=>{if(!currentChunk)return;currentChunk.changes.push({type:"del",del:true,ln:deletedLineCounter++,content:line});currentFile.deletions++;currentFileChanges.oldLines--};const add=line=>{if(!currentChunk)return;currentChunk.changes.push({type:"add",add:true,ln:addedLineCounter++,content:line});currentFile.additions++;currentFileChanges.newLines--};const eof=line=>{if(!currentChunk)return;const[mostRecentChange]=currentChunk.changes.slice(-1);currentChunk.changes.push({type:mostRecentChange.type,[mostRecentChange.type]:true,ln1:mostRecentChange.ln1,ln2:mostRecentChange.ln2,ln:mostRecentChange.ln,content:line})};const schemaHeaders=[[/^diff\s/,start],[/^new file mode \d+$/,newFile],[/^deleted file mode \d+$/,deletedFile],[/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/,index],[/^---\s/,fromFile],[/^\+\+\+\s/,toFile],[/^@@\s+-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/,chunk],[/^\\ No newline at end of file$/,eof]];const schemaContent=[[/^-/,del],[/^\+/,add],[/^\s+/,normal]];const parseContentLine=line=>{for(const[pattern,handler]of schemaContent){const match=line.match(pattern);if(match){handler(line,match);break}}if(currentFileChanges.oldLines===0&&currentFileChanges.newLines===0){currentFileChanges=null}};const parseHeaderLine=line=>{for(const[pattern,handler]of schemaHeaders){const match=line.match(pattern);if(match){handler(line,match);break}}};const parseLine=line=>{if(currentFileChanges){parseContentLine(line)}else{parseHeaderLine(line)}return};for(const line of lines)parseLine(line);return files};const fileNameDiffRegex=/a\/.*(?=["']? ["']?b\/)|b\/.*$/g;const gitFileHeaderRegex=/^(a|b)\//;const parseFiles=line=>{let fileNames=line===null||line===void 0?void 0:line.match(fileNameDiffRegex);return fileNames===null||fileNames===void 0?void 0:fileNames.map(fileName=>fileName.replace(gitFileHeaderRegex,"").replace(/("|')$/,""))};const qoutedFileNameRegex=/^\\?['"]|\\?['"]$/g;const parseOldOrNewFile=line=>{let fileName=leftTrimChars(line,"-+").trim();fileName=removeTimeStamp(fileName);return fileName.replace(qoutedFileNameRegex,"").replace(gitFileHeaderRegex,"")};const leftTrimChars=(string,trimmingChars)=>{string=makeString(string);if(!trimmingChars&&String.prototype.trimLeft)return string.trimLeft();let trimmingString=formTrimmingString(trimmingChars);return string.replace(new RegExp(`^${trimmingString}+`),"")};const timeStampRegex=/\t.*|\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d(.\d+)?\s(\+|-)\d\d\d\d/;const removeTimeStamp=string=>{const timeStamp=timeStampRegex.exec(string);if(timeStamp){string=string.substring(0,timeStamp.index).trim()}return string};const formTrimmingString=trimmingChars=>{if(trimmingChars===null||trimmingChars===undefined)return"\\s";else if(trimmingChars instanceof RegExp)return trimmingChars.source;return`[${makeString(trimmingChars).replace(/([.*+?^=!:${}()|[\]/\\])/g,"\\$1")}]`};const makeString=itemToConvert=>(itemToConvert??"")+"";