Skip to content

Commit

Permalink
feat: add support for recursive to readdir (#972)
Browse files Browse the repository at this point in the history
* Add path to Dirent and recursive to options.  Fails tests because it's not detecting children's directories.

* Add path to Dirent and recursive option to readdirBase

* Typo

* Add more files to tests

---------

Co-authored-by: Michael Cole <gitlab@michaelcole.com>
  • Loading branch information
MichaelJCole and Michael Cole committed Feb 12, 2024
1 parent 9e38dd5 commit 919ddfc
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 25 deletions.
2 changes: 2 additions & 0 deletions src/Dirent.ts
Expand Up @@ -15,11 +15,13 @@ export class Dirent implements IDirent {

dirent.name = strToEncoding(link.getName(), encoding);
dirent.mode = mode;
dirent.path = link.getPath()

return dirent;
}

name: TDataOut = '';
path = '';
private mode: number = 0;

private _checkModeProperty(property: number): boolean {
Expand Down
57 changes: 57 additions & 0 deletions src/__tests__/volume/readdirSync.test.ts
Expand Up @@ -55,4 +55,61 @@ describe('readdirSync()', () => {

expect(dirs).toEqual(['foo']);
});

it('accepts option {withFileTypes: true}', () => {
const vol = create({
'/x/af': 'a',
'/x/b/bf': 'b',
'/x/c/c/cf': 'c',
});
const all = vol.readdirSync('/x', { withFileTypes: true });
const mapped = all.map((dirent) => { return {...dirent} })
expect(mapped).toEqual([
{ mode: 33206, name: 'af', path: '/x/af' },
{ mode: 16895, name: 'b', path: '/x/b' },
{ mode: 16895, name: 'c', path: '/x/c' },
]);
});

it('accepts option {recursive: true}', () => {
const vol = create({
'/y/af1': 'a',
'/y/af2': 'a',
'/y/b/bf1': 'b',
'/y/b/bf2': 'b',
'/y/c/c/.cf0': 'c',
'/y/c/c/cf1': 'c',
'/y/c/c/cf2': 'c',
});
const all = vol.readdirSync('/y', { recursive: true });
(all as any).sort();
expect(all).toEqual(['af1', 'af2', 'b', 'b/bf1', 'b/bf2', 'c', 'c/c', 'c/c/.cf0', 'c/c/cf1', 'c/c/cf2']);
});

it('accepts option {recursive: true, withFileTypes: true}', () => {
const vol = create({
'/z/af1': 'a',
'/z/af2': 'a',
'/z/b/bf1': 'b',
'/z/b/bf2': 'b',
'/z/c/c/.cf0': 'c',
'/z/c/c/cf1': 'c',
'/z/c/c/cf2': 'c',
});
const all = vol.readdirSync('/z', { recursive: true, withFileTypes: true });
const mapped = all.map((dirent) => { return {...dirent} })
expect(mapped).toEqual([
{ mode: 33206, name: '.cf0', path: '/z/c/c/.cf0' },
{ mode: 33206, name: 'af1', path: '/z/af1' },
{ mode: 33206, name: 'af2', path: '/z/af2' },
{ mode: 16895, name: 'b', path: '/z/b' },
{ mode: 33206, name: 'bf1', path: '/z/b/bf1' },
{ mode: 33206, name: 'bf2', path: '/z/b/bf2' },
{ mode: 16895, name: 'c', path: '/z/c' },
{ mode: 16895, name: 'c', path: '/z/c/c' },
{ mode: 33206, name: 'cf1', path: '/z/c/c/cf1' },
{ mode: 33206, name: 'cf2', path: '/z/c/c/cf2' },
]);
});

});
1 change: 1 addition & 0 deletions src/node/options.ts
Expand Up @@ -73,6 +73,7 @@ export const getReadFileOptions = optsGenerator<opts.IReadFileOptions>(readFileO

const readdirDefaults: opts.IReaddirOptions = {
encoding: 'utf8',
recursive: false,
withFileTypes: false,
};
export const getReaddirOptions = optsGenerator<opts.IReaddirOptions>(readdirDefaults);
Expand Down
1 change: 1 addition & 0 deletions src/node/types/options.ts
Expand Up @@ -33,6 +33,7 @@ export interface IFStatOptions {
export interface IAppendFileOptions extends IFileOptions {}

export interface IReaddirOptions extends IOptions {
recursive?: boolean
withFileTypes?: boolean;
}

Expand Down
53 changes: 28 additions & 25 deletions src/volume.ts
Expand Up @@ -1462,37 +1462,40 @@ export class Volume implements FsCallbackApi, FsSynchronousApi {
const node = link.getNode();
if (!node.isDirectory()) throw createError(ENOTDIR, 'scandir', filename);

if (options.withFileTypes) {
const list: Dirent[] = [];
for (const name of link.children.keys()) {
const child = link.getChild(name);
const list: Dirent[] = []; // output list

if (!child || name === '.' || name === '..') {
continue;
}

list.push(Dirent.build(child, options.encoding));
}
if (!isWin && options.encoding !== 'buffer')
list.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
return list;
}

const list: TDataOut[] = [];
for (const name of link.children.keys()) {
if (name === '.' || name === '..') {
continue;
const child = link.getChild(name);

if (!child || name === '.' || name === '..') continue;

list.push(Dirent.build(child, options.encoding));

// recursion
if (options.recursive && child.children.size) {
const recurseOptions = { ...options, recursive: true, withFileTypes: true }
const childList = this.readdirBase(child.getPath(), recurseOptions) as Dirent[]
list.push(...childList)
}
list.push(strToEncoding(name, options.encoding));
}

if (!isWin && options.encoding !== 'buffer') list.sort();
if (!isWin && options.encoding !== 'buffer')
list.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});

if (options.withFileTypes) return list;

const ret: TDataOut[] = [];

return list;
return list.map(dirent => {
if (options.recursive) {
return dirent.path.replace(filename + pathModule.sep, '')
}
return dirent.name
})
}

readdirSync(path: PathLike, options?: opts.IReaddirOptions | string): TDataOut[] | Dirent[] {
Expand Down

0 comments on commit 919ddfc

Please sign in to comment.