Skip to content

Commit

Permalink
Fix: switch to typescript build
Browse files Browse the repository at this point in the history
  • Loading branch information
gliviu committed Mar 11, 2021
1 parent 18b0251 commit 8fb74dd
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 133 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
"build/src"
],
"scripts": {
"prepublishOnly": "npm run clean && npm run build",
"prepublishOnly": "npm run clean && npm run build && npm run lint",
"clean": "rm -rf build && rm -rf .nyc_output && rm -rf coverage",
"copydeps": "copyfiles \"test/expected/**\" test/testdir.tar \"test/extended/res/**\" build",
"build": "tsc && npm run lint && npm run copydeps",
"build": "tsc && npm run copydeps",
"lint": "eslint ./src ./test --ext .ts,.js",
"pretest": "npm install && npm run build",
"test": "node build/test/runTests.js",
"extest": "npm run pretest && ./test/extended/init.sh && test/extended/runall.sh",
"coverage": "npx nyc --exclude \"build/test/**\" --reporter=lcov npm test && npx nyc report",
"toc": "npx markdown-toc README.md; echo \n",
"docs": "typedoc --includeVersion --excludeExternals --theme minimal --readme none --gitRevision master --toc compare,compareSync,fileCompareHandlers,Options,Result --out docs ./src/index.d.ts"
"docs": "typedoc --includeVersion --excludeExternals --theme minimal --readme none --gitRevision master --toc compare,compareSync,fileCompareHandlers,Options,Result --out docs ./src/index.ts"
},
"dependencies": {
"buffer-equal": "^1.0.0",
Expand Down
21 changes: 21 additions & 0 deletions src/FileCompareHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { CompareFileHandler } from './types';


export interface FileCompareHandlers {
/**
* Default file content comparison handlers, used if [[Options.compareFileAsync]] or [[Options.compareFileSync]] are not specified.
*
* Performs binary comparison.
*/
defaultFileCompare: CompareFileHandler;
/**
* Compares files line by line.
*
* Options:
* * ignoreLineEnding - true/false (default: false) - Ignore cr/lf line endings
* * ignoreWhiteSpaces - true/false (default: false) - Ignore white spaces at the beginning and ending of a line (similar to 'diff -b')
* * ignoreAllWhiteSpaces - true/false (default: false) - Ignore all white space differences (similar to 'diff -w')
* * ignoreEmptyLines - true/false (default: false) - Ignores differences caused by empty lines (similar to 'diff -B')
*/
lineBasedFileCompare: CompareFileHandler;
}
151 changes: 76 additions & 75 deletions src/fileCompareHandler/default/defaultFileCompare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,91 +5,95 @@ import fsPromise from '../../fs/fsPromise'
import { BufferPair, BufferPool } from '../../fs/BufferPool'
import closeFile from '../../fs/closeFile'
import { Options } from '../../index'
import { CompareFileHandler } from '../../types'

const MAX_CONCURRENT_FILE_COMPARE = 8
const BUF_SIZE = 100000
const fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2)
const bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE) // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently


/**
* Compares two files by content.
*/
function compareSync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): boolean {
let fd1: number | undefined
let fd2: number | undefined
if (stat1.size !== stat2.size) {
return false
}
const bufferPair = bufferPool.allocateBuffers()
try {
fd1 = fs.openSync(path1, 'r')
fd2 = fs.openSync(path2, 'r')
const buf1 = bufferPair.buf1
const buf2 = bufferPair.buf2
for (; ;) {
const size1 = fs.readSync(fd1, buf1, 0, BUF_SIZE, null)
const size2 = fs.readSync(fd2, buf2, 0, BUF_SIZE, null)
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
export const defaultFileCompare: CompareFileHandler = {
/**
* Compares two files by content.
*/
compareSync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): boolean {
let fd1: number | undefined
let fd2: number | undefined
if (stat1.size !== stat2.size) {
return false
}
const bufferPair = bufferPool.allocateBuffers()
try {
fd1 = fs.openSync(path1, 'r')
fd2 = fs.openSync(path2, 'r')
const buf1 = bufferPair.buf1
const buf2 = bufferPair.buf2
for (; ;) {
const size1 = fs.readSync(fd1, buf1, 0, BUF_SIZE, null)
const size2 = fs.readSync(fd2, buf2, 0, BUF_SIZE, null)
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
}
}
} finally {
closeFile.closeFilesSync(fd1, fd2)
bufferPool.freeBuffers(bufferPair)
}
} finally {
closeFile.closeFilesSync(fd1, fd2)
bufferPool.freeBuffers(bufferPair)
}
}
},


/**
* Compares two files by content
*/
function compareAsync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): Promise<boolean> {
let fd1: number | undefined
let fd2: number | undefined
let bufferPair: BufferPair | undefined
if (stat1.size !== stat2.size) {
return Promise.resolve(false)
/**
* Compares two files by content
*/
async compareAsync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): Promise<boolean> {
let fd1: number | undefined
let fd2: number | undefined
let bufferPair: BufferPair | undefined
if (stat1.size !== stat2.size) {
return Promise.resolve(false)
}
return Promise.all([fdQueue.openPromise(path1, 'r'), fdQueue.openPromise(path2, 'r')])
.then(fds => {
bufferPair = bufferPool.allocateBuffers()
fd1 = fds[0]
fd2 = fds[1]
const buf1 = bufferPair.buf1
const buf2 = bufferPair.buf2
const compareAsyncInternal = () => Promise.all([
fsPromise.read(fd1, buf1, 0, BUF_SIZE, null),
fsPromise.read(fd2, buf2, 0, BUF_SIZE, null)
])
.then((bufferSizes) => {
const size1 = bufferSizes[0]
const size2 = bufferSizes[1]
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
} else {
return compareAsyncInternal()
}
})
return compareAsyncInternal()
})
.then(
// 'finally' polyfill for node 8 and below
res => finalizeAsync(fd1, fd2, bufferPair).then(() => res),
err => finalizeAsync(fd1, fd2, bufferPair).then(() => { throw err })
)
}
return Promise.all([fdQueue.openPromise(path1, 'r'), fdQueue.openPromise(path2, 'r')])
.then(fds => {
bufferPair = bufferPool.allocateBuffers()
fd1 = fds[0]
fd2 = fds[1]
const buf1 = bufferPair.buf1
const buf2 = bufferPair.buf2
const compareAsyncInternal = () => Promise.all([
fsPromise.read(fd1, buf1, 0, BUF_SIZE, null),
fsPromise.read(fd2, buf2, 0, BUF_SIZE, null)
])
.then((bufferSizes) => {
const size1 = bufferSizes[0]
const size2 = bufferSizes[1]
if (size1 !== size2) {
return false
} else if (size1 === 0) {
// End of file reached
return true
} else if (!compareBuffers(buf1, buf2, size1)) {
return false
} else {
return compareAsyncInternal()
}
})
return compareAsyncInternal()
})
.then(
// 'finally' polyfill for node 8 and below
res => finalizeAsync(fd1, fd2, bufferPair).then(() => res),
err => finalizeAsync(fd1, fd2, bufferPair).then(() => { throw err })
)

}


function compareBuffers(buf1: Buffer, buf2: Buffer, contentSize: number) {
return bufferEqual(buf1.slice(0, contentSize), buf2.slice(0, contentSize))
}
Expand All @@ -101,6 +105,3 @@ function finalizeAsync(fd1?: number, fd2?: number, bufferPair?: BufferPair) {
return closeFile.closeFilesAsync(fd1, fd2, fdQueue)
}

export default {
compareSync, compareAsync
}
3 changes: 2 additions & 1 deletion src/fileCompareHandler/lines/compareAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { LineBasedCompareContext } from './LineBasedCompareContext'
import { BufferPool } from '../../fs/BufferPool'
import { compareLineBatches } from './compare/compareLineBatches'
import { readBufferedLines } from './lineReader/readBufferedLines'
import { CompareFileAsync } from '../../types'

const BUF_SIZE = 100000
const MAX_CONCURRENT_FILE_COMPARE = 8

const fdQueue = new FileDescriptorQueue(MAX_CONCURRENT_FILE_COMPARE * 2)
const bufferPool = new BufferPool(BUF_SIZE, MAX_CONCURRENT_FILE_COMPARE) // fdQueue guarantees there will be no more than MAX_CONCURRENT_FILE_COMPARE async processes accessing the buffers concurrently

export default async function compareAsync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): Promise<boolean> {
export const lineBasedCompareAsync: CompareFileAsync = async (path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): Promise<boolean> => {
const bufferSize = Math.min(BUF_SIZE, options.lineBasedHandlerBufferSize ?? Number.MAX_VALUE)
let context: LineBasedCompareContext | undefined
try {
Expand Down
3 changes: 2 additions & 1 deletion src/fileCompareHandler/lines/compareSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { compareLineBatches } from './compare/compareLineBatches'
import { readBufferedLines } from './lineReader/readBufferedLines'
import { BufferPair } from '../../fs/BufferPool'
import { LineBatch } from './lineReader/LineBatch'
import { CompareFileSync } from '../../types'

const BUF_SIZE = 100000

Expand All @@ -15,7 +16,7 @@ const bufferPair: BufferPair = {
busy: true
}

export default function compareSync(path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): boolean {
export const lineBasedCompareSync: CompareFileSync = (path1: string, stat1: fs.Stats, path2: string, stat2: fs.Stats, options: Options): boolean => {
const bufferSize = Math.min(BUF_SIZE, options.lineBasedHandlerBufferSize ?? Number.MAX_VALUE)
let context: LineBasedCompareContext | undefined
try {
Expand Down
12 changes: 6 additions & 6 deletions src/fileCompareHandler/lines/lineBasedFileCompare.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import compareSync from './compareSync'
import compareAsync from './compareAsync'


import {lineBasedCompareSync} from './compareSync'
import {lineBasedCompareAsync} from './compareAsync'
import { CompareFileHandler } from '../../types'

/**
* Compare files line by line with options to ignore
* line endings and white space differences.
*/
export default {
compareSync, compareAsync
export const lineBasedFileCompare: CompareFileHandler = {
compareSync: lineBasedCompareSync,
compareAsync: lineBasedCompareAsync
}
76 changes: 29 additions & 47 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,19 @@ import fs from 'fs'
import compareSyncInternal from './compareSync'
import compareAsyncInternal from './compareAsync'
import defaultResultBuilderCallback from './resultBuilder/defaultResultBuilderCallback'
import defaultFileCompare from './fileCompareHandler/default/defaultFileCompare'
import lineBasedFileCompare from './fileCompareHandler/lines/lineBasedFileCompare'
import { defaultFileCompare } from './fileCompareHandler/default/defaultFileCompare'
import { lineBasedFileCompare } from './fileCompareHandler/lines/lineBasedFileCompare'
import defaultNameCompare from './nameCompare/defaultNameCompare'
import entryBuilder from './entry/entryBuilder'
import statsLifecycle from './statistics/statisticsLifecycle'
import loopDetector from './symlink/loopDetector'
import { CompareFileHandler, Options, Result } from './types'
import { Options, Result } from './types'
import { FileCompareHandlers } from './FileCompareHandlers'

const ROOT_PATH = pathUtils.sep

export * from './types'

/**
* Available file content comparison handlers.
* These handlers are used when [[Options.compareContent]] is set.
*/
export interface FileCompareHandlers {
/**
* Default file content comparison handlers, used if [[Options.compareFileAsync]] or [[Options.compareFileSync]] are not specified.
*
* Performs binary comparison.
*/
defaultFileCompare: CompareFileHandler,
/**
* Compares files line by line.
*
* Options:
* * ignoreLineEnding - true/false (default: false) - Ignore cr/lf line endings
* * ignoreWhiteSpaces - true/false (default: false) - Ignore white spaces at the beginning and ending of a line (similar to 'diff -b')
* * ignoreAllWhiteSpaces - true/false (default: false) - Ignore all white space differences (similar to 'diff -w')
* * ignoreEmptyLines - true/false (default: false) - Ignores differences caused by empty lines (similar to 'diff -B')
*/
lineBasedFileCompare: CompareFileHandler
}

export const fileCompareHandlers: FileCompareHandlers = {
defaultFileCompare: defaultFileCompare,
lineBasedFileCompare: lineBasedFileCompare
}

/**
* Synchronously compares given paths.
* @param path1 Left file or directory to be compared.
Expand All @@ -69,22 +42,6 @@ export function compareSync(path1: string, path2: string, options?: Options): Re
return statistics as unknown as Result
}

type RealPathOptions = { encoding?: BufferEncoding | null } | BufferEncoding

const wrapper = {
realPath(path: string, options?: RealPathOptions): Promise<string> {
return new Promise((resolve, reject) => {
fs.realpath(path, options, (err, resolvedPath) => {
if (err) {
reject(err)
} else {
resolve(resolvedPath)
}
})
})
}
}

/**
* Asynchronously compares given paths.
* @param path1 Left file or directory to be compared.
Expand Down Expand Up @@ -125,6 +82,31 @@ export function compare(path1: string, path2: string, options?: Options): Promis
})
}

/**
* File comparison handlers.
* These handlers are used when [[Options.compareContent]] is set.
*/
export const fileCompareHandlers: FileCompareHandlers = {
defaultFileCompare,
lineBasedFileCompare
}

type RealPathOptions = { encoding?: BufferEncoding | null } | BufferEncoding

const wrapper = {
realPath(path: string, options?: RealPathOptions): Promise<string> {
return new Promise((resolve, reject) => {
fs.realpath(path, options, (err, resolvedPath) => {
if (err) {
reject(err)
} else {
resolve(resolvedPath)
}
})
})
}
}

function prepareOptions(options?: Options): Options {
options = options || {}
const clone = JSON.parse(JSON.stringify(options))
Expand Down

0 comments on commit 8fb74dd

Please sign in to comment.