@@ -11,7 +11,6 @@ import {
1111import path from "node:path" ;
1212import fs from "node:fs" ;
1313import * as fsp from 'node:fs/promises' ;
14- import os from "node:os" ;
1514import { RegexRule , RegexRules } from "./config" ;
1615import { isBinaryFile } from "isbinaryfile" ;
1716import { convertToRegex , PromiseExecutionLimiter } from "./utils" ;
@@ -137,7 +136,8 @@ export class RegexEngine extends Engine {
137136 private async scanFileContents ( fileName : string , fileContents : string , ruleName : string ) : Promise < Violation [ ] > {
138137 const violations : Violation [ ] = [ ] ;
139138 const regex : RegExp = this . getRegExpFor ( ruleName ) ;
140- const newlineIndexes : number [ ] = getNewlineIndices ( fileContents ) ;
139+ const contextuallyDerivedEol : string = contextuallyDeriveEolString ( fileContents ) ;
140+ const newlineIndexes : number [ ] = getNewlineIndices ( fileContents , contextuallyDerivedEol ) ;
141141
142142 for ( const match of fileContents . matchAll ( regex ) ) {
143143 let startIndex : number = match . index ;
@@ -151,9 +151,9 @@ export class RegexEngine extends Engine {
151151 }
152152
153153 const startLine : number = getLineNumber ( startIndex , newlineIndexes ) ;
154- const startColumn : number = getColumnNumber ( startIndex , newlineIndexes ) ;
154+ const startColumn : number = getColumnNumber ( startIndex , newlineIndexes , contextuallyDerivedEol ) ;
155155 const endLine : number = getLineNumber ( startIndex + matchLength , newlineIndexes ) ;
156- const endColumn : number = getColumnNumber ( startIndex + matchLength , newlineIndexes ) ;
156+ const endColumn : number = getColumnNumber ( startIndex + matchLength , newlineIndexes , contextuallyDerivedEol ) ;
157157 const codeLocation : CodeLocation = {
158158 file : fileName ,
159159 startLine : startLine ,
@@ -180,13 +180,13 @@ export class RegexEngine extends Engine {
180180}
181181
182182
183- function getColumnNumber ( charIndex : number , newlineIndexes : number [ ] ) : number {
183+ function getColumnNumber ( charIndex : number , newlineIndexes : number [ ] , contextuallyDerivedEol : string ) : number {
184184 const idxOfNextNewline = newlineIndexes . findIndex ( el => el >= charIndex ) ;
185185 const idxOfCurrentLine = idxOfNextNewline === - 1 ? newlineIndexes . length - 1 : idxOfNextNewline - 1 ;
186186 if ( idxOfCurrentLine === 0 ) {
187187 return charIndex + 1 ;
188188 } else {
189- const eolOffset = os . EOL . length - 1 ;
189+ const eolOffset = contextuallyDerivedEol . length - 1 ;
190190 return charIndex - newlineIndexes . at ( idxOfCurrentLine ) ! - eolOffset ;
191191 }
192192}
@@ -196,8 +196,8 @@ function getLineNumber(charIndex: number, newlineIndexes: number[]): number{
196196 return idxOfNextNewline === - 1 ? newlineIndexes . length : idxOfNextNewline ;
197197}
198198
199- function getNewlineIndices ( fileContents : string ) : number [ ] {
200- const newlineRegex : RegExp = new RegExp ( os . EOL , "g" ) ;
199+ function getNewlineIndices ( fileContents : string , contextuallyDerivedEol : string ) : number [ ] {
200+ const newlineRegex : RegExp = new RegExp ( contextuallyDerivedEol , "g" ) ;
201201 const matches = fileContents . matchAll ( newlineRegex ) ;
202202 const newlineIndexes = [ - 1 ] ;
203203
@@ -207,6 +207,15 @@ function getNewlineIndices(fileContents: string): number[] {
207207 return newlineIndexes ;
208208}
209209
210+ /**
211+ * We can't assume that the file's EOL indicator matches the OS's, because of edge cases such as a file created on Unix
212+ * but scanned on Windows. So instead of consulting the OS for the EOL indicator, we look at the file and find which one it uses.
213+ * @param contents
214+ */
215+ function contextuallyDeriveEolString ( contents : string ) : string {
216+ return contents . indexOf ( '\r\n' ) !== - 1 ? '\r\n' : '\n' ;
217+ }
218+
210219async function isTextFile ( fileName : string ) : Promise < boolean > {
211220 const ext : string = path . extname ( fileName ) . toLowerCase ( ) ;
212221 return TEXT_BASED_FILE_EXTS . has ( ext ) || ! ( await isBinaryFile ( fileName ) ) ;
0 commit comments