Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions __test__/readdir.spec.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,67 @@
import test from 'ava'
import { readdirSync, readdir } from '../index.js'

test('sync: should list files in current directory', (t) => {
test('sync: should list files in current directory (strings by default)', (t) => {
const files = readdirSync('.')

t.true(Array.isArray(files))
t.true(files.length > 0)

// Verify it returns strings
t.is(typeof files[0], 'string')

const packageJson = files.find((f) => f === 'package.json')
t.truthy(packageJson, 'Result should contain package.json')
})

test('sync: should return Dirent objects when withFileTypes is true', (t) => {
const files = readdirSync('.', { withFileTypes: true })

t.true(Array.isArray(files))
t.true(files.length > 0)

// Verify Dirent structure
const packageJson = files.find((f) => f.name === 'package.json')
// We need to cast or check type because typescript might infer union type
const first = files[0]
if (typeof first === 'object') {
t.is(typeof first.name, 'string')
t.is(typeof first.isDir, 'boolean')
} else {
t.fail('Should return objects when withFileTypes is true')
}

const packageJson = files.find((f) => typeof f !== 'string' && f.name === 'package.json')
t.truthy(packageJson, 'Result should contain package.json')
t.is(packageJson?.isDir, false)
t.true(packageJson?.path.includes('package.json'))

const srcDir = files.find((f) => f.name === 'src')
if (srcDir) {
if (typeof packageJson !== 'string' && packageJson) {
t.is(packageJson.isDir, false)
}

const srcDir = files.find((f) => typeof f !== 'string' && f.name === 'src')
if (srcDir && typeof srcDir !== 'string') {
t.is(srcDir.isDir, true, 'src should be identified as a directory')
}
})

test('async: should list files in current directory', async (t) => {
const files = await readdir('.')
t.true(files.length > 0)
t.truthy(files.find((f) => f.name === 'package.json'))
t.is(typeof files[0], 'string')
t.truthy(files.find((f) => f === 'package.json'))
})

test('concurrency: run with specific thread count', (t) => {
const files = readdirSync('.', {
concurrency: 4,
recursive: true, // concurrency only works with recursive/walk_dir
})
t.true(files.length > 0)
})

test('concurrency: run with high thread count (stress test)', (t) => {
const files = readdirSync('.', {
concurrency: 100,
recursive: true,
})
t.true(files.length > 0)
})
Expand All @@ -44,11 +71,12 @@ test('options: skip_hidden should filter out dotfiles', (t) => {
// but based on your rust code, default is false)
const allFiles = readdirSync('.', { skipHidden: false })
// Assuming this repo has a .git folder or similar
const hasHidden = allFiles.some((f) => f.name.startsWith('.'))
// files are strings now
const hasHidden = allFiles.some((f) => (typeof f === 'string' ? f : f.name).startsWith('.'))

if (hasHidden) {
const visibleFiles = readdirSync('.', { skipHidden: true })
const hiddenRemains = visibleFiles.some((f) => f.name.startsWith('.'))
const hiddenRemains = visibleFiles.some((f) => (typeof f === 'string' ? f : f.name).startsWith('.'))
t.false(hiddenRemains, 'Should not contain hidden files when skip_hidden is true')
} else {
t.pass('No hidden files found in root to test skipping')
Expand Down
22 changes: 18 additions & 4 deletions benchmark/readdir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,32 @@ console.log(`Benchmarking readdir on: ${dir}`)

bench
.add('Node.js fs.readdirSync', () => {
fs.readdirSync(dir)
})
.add('Node.js fs.readdirSync (withFileTypes)', () => {
fs.readdirSync(dir, { withFileTypes: true })
})
.add('Node.js fs.readdirSync (recursive)', () => {
.add('Node.js fs.readdirSync (recursive, withFileTypes)', () => {
fs.readdirSync(dir, { recursive: true, withFileTypes: true })
})
.add('hyper-fs readdirSync (default)', () => {
readdirSync(dir)
})
.add('hyper-fs readdirSync (4 threads)', () => {
readdirSync(dir, { concurrency: 4 })
.add('hyper-fs readdirSync (withFileTypes)', () => {
readdirSync(dir, { withFileTypes: true })
})
.add('hyper-fs readdirSync (recursive)', () => {
readdirSync(dir, { recursive: true })
})
.add('hyper-fs readdirSync (recursive, withFileTypes)', () => {
readdirSync(dir, { recursive: true, withFileTypes: true })
})
.add('hyper-fs readdirSync (4 threads, recursive)', () => {
readdirSync(dir, { concurrency: 4, recursive: true })
})
.add('hyper-fs readdirSync (4 threads, recursive, withFileTypes)', () => {
readdirSync(dir, { concurrency: 4, recursive: true, withFileTypes: true })
})

await bench.run()

console.table(bench.table())
9 changes: 7 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable */
export interface Dirent {
name: string
path: string
parentPath: string
isDir: boolean
}

Expand All @@ -11,6 +11,11 @@ export declare function readdir(path: string, options?: ReaddirOptions | undefin
export interface ReaddirOptions {
skipHidden?: boolean
concurrency?: number
recursive?: boolean
withFileTypes?: boolean
}

export declare function readdirSync(path: string, options?: ReaddirOptions | undefined | null): Array<Dirent>
export declare function readdirSync(
path: string,
options?: ReaddirOptions | undefined | null,
): Array<string> | Array<Dirent>
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#![deny(clippy::all)]

// define modules
pub mod read_dir;
pub mod readdir;

//export modules
pub use read_dir::*;
pub use readdir::*;
108 changes: 0 additions & 108 deletions src/read_dir.rs

This file was deleted.

Loading