Skip to content

Commit b2a79ec

Browse files
authored
Merge pull request #1 from CoderSerio/feat/readdir
feat: optimize readdir performance and align API with Node.js
2 parents 09f9a95 + 78010bc commit b2a79ec

File tree

6 files changed

+245
-125
lines changed

6 files changed

+245
-125
lines changed

__test__/readdir.spec.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,67 @@
11
import test from 'ava'
22
import { readdirSync, readdir } from '../index.js'
33

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

77
t.true(Array.isArray(files))
88
t.true(files.length > 0)
99

10+
// Verify it returns strings
11+
t.is(typeof files[0], 'string')
12+
13+
const packageJson = files.find((f) => f === 'package.json')
14+
t.truthy(packageJson, 'Result should contain package.json')
15+
})
16+
17+
test('sync: should return Dirent objects when withFileTypes is true', (t) => {
18+
const files = readdirSync('.', { withFileTypes: true })
19+
20+
t.true(Array.isArray(files))
21+
t.true(files.length > 0)
22+
1023
// Verify Dirent structure
11-
const packageJson = files.find((f) => f.name === 'package.json')
24+
// We need to cast or check type because typescript might infer union type
25+
const first = files[0]
26+
if (typeof first === 'object') {
27+
t.is(typeof first.name, 'string')
28+
t.is(typeof first.isDir, 'boolean')
29+
} else {
30+
t.fail('Should return objects when withFileTypes is true')
31+
}
32+
33+
const packageJson = files.find((f) => typeof f !== 'string' && f.name === 'package.json')
1234
t.truthy(packageJson, 'Result should contain package.json')
13-
t.is(packageJson?.isDir, false)
14-
t.true(packageJson?.path.includes('package.json'))
1535

16-
const srcDir = files.find((f) => f.name === 'src')
17-
if (srcDir) {
36+
if (typeof packageJson !== 'string' && packageJson) {
37+
t.is(packageJson.isDir, false)
38+
}
39+
40+
const srcDir = files.find((f) => typeof f !== 'string' && f.name === 'src')
41+
if (srcDir && typeof srcDir !== 'string') {
1842
t.is(srcDir.isDir, true, 'src should be identified as a directory')
1943
}
2044
})
2145

2246
test('async: should list files in current directory', async (t) => {
2347
const files = await readdir('.')
2448
t.true(files.length > 0)
25-
t.truthy(files.find((f) => f.name === 'package.json'))
49+
t.is(typeof files[0], 'string')
50+
t.truthy(files.find((f) => f === 'package.json'))
2651
})
2752

2853
test('concurrency: run with specific thread count', (t) => {
2954
const files = readdirSync('.', {
3055
concurrency: 4,
56+
recursive: true, // concurrency only works with recursive/walk_dir
3157
})
3258
t.true(files.length > 0)
3359
})
3460

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

4977
if (hasHidden) {
5078
const visibleFiles = readdirSync('.', { skipHidden: true })
51-
const hiddenRemains = visibleFiles.some((f) => f.name.startsWith('.'))
79+
const hiddenRemains = visibleFiles.some((f) => (typeof f === 'string' ? f : f.name).startsWith('.'))
5280
t.false(hiddenRemains, 'Should not contain hidden files when skip_hidden is true')
5381
} else {
5482
t.pass('No hidden files found in root to test skipping')

benchmark/readdir.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,32 @@ console.log(`Benchmarking readdir on: ${dir}`)
1111

1212
bench
1313
.add('Node.js fs.readdirSync', () => {
14+
fs.readdirSync(dir)
15+
})
16+
.add('Node.js fs.readdirSync (withFileTypes)', () => {
1417
fs.readdirSync(dir, { withFileTypes: true })
1518
})
16-
.add('Node.js fs.readdirSync (recursive)', () => {
19+
.add('Node.js fs.readdirSync (recursive, withFileTypes)', () => {
1720
fs.readdirSync(dir, { recursive: true, withFileTypes: true })
1821
})
1922
.add('hyper-fs readdirSync (default)', () => {
2023
readdirSync(dir)
2124
})
22-
.add('hyper-fs readdirSync (4 threads)', () => {
23-
readdirSync(dir, { concurrency: 4 })
25+
.add('hyper-fs readdirSync (withFileTypes)', () => {
26+
readdirSync(dir, { withFileTypes: true })
27+
})
28+
.add('hyper-fs readdirSync (recursive)', () => {
29+
readdirSync(dir, { recursive: true })
30+
})
31+
.add('hyper-fs readdirSync (recursive, withFileTypes)', () => {
32+
readdirSync(dir, { recursive: true, withFileTypes: true })
33+
})
34+
.add('hyper-fs readdirSync (4 threads, recursive)', () => {
35+
readdirSync(dir, { concurrency: 4, recursive: true })
36+
})
37+
.add('hyper-fs readdirSync (4 threads, recursive, withFileTypes)', () => {
38+
readdirSync(dir, { concurrency: 4, recursive: true, withFileTypes: true })
2439
})
25-
2640
await bench.run()
2741

2842
console.table(bench.table())

index.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/* eslint-disable */
33
export interface Dirent {
44
name: string
5-
path: string
5+
parentPath: string
66
isDir: boolean
77
}
88

@@ -11,6 +11,11 @@ export declare function readdir(path: string, options?: ReaddirOptions | undefin
1111
export interface ReaddirOptions {
1212
skipHidden?: boolean
1313
concurrency?: number
14+
recursive?: boolean
15+
withFileTypes?: boolean
1416
}
1517

16-
export declare function readdirSync(path: string, options?: ReaddirOptions | undefined | null): Array<Dirent>
18+
export declare function readdirSync(
19+
path: string,
20+
options?: ReaddirOptions | undefined | null,
21+
): Array<string> | Array<Dirent>

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#![deny(clippy::all)]
1111

1212
// define modules
13-
pub mod read_dir;
13+
pub mod readdir;
1414

1515
//export modules
16-
pub use read_dir::*;
16+
pub use readdir::*;

src/read_dir.rs

Lines changed: 0 additions & 108 deletions
This file was deleted.

0 commit comments

Comments
 (0)