Skip to content

Commit

Permalink
feat: 🎸 implement Dirent listings
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 16, 2023
1 parent 685bc7e commit 5d6f976
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 6 deletions.
3 changes: 2 additions & 1 deletion src/Dirent.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Link } from './node';
import { constants } from './constants';
import { TEncodingExtended, strToEncoding, TDataOut } from './encoding';
import type {IDirent} from './node/types/misc';

const { S_IFMT, S_IFDIR, S_IFREG, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO, S_IFSOCK } = constants;

/**
* A directory entry, like `fs.Dirent`.
*/
export class Dirent {
export class Dirent implements IDirent {
static build(link: Link, encoding: TEncodingExtended | undefined) {
const dirent = new Dirent();
const { mode } = link.getNode();
Expand Down
35 changes: 35 additions & 0 deletions src/fsa-to-node/FsaNodeDirent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {NodeFileSystemDirectoryHandle, NodeFileSystemFileHandle} from "../node-to-fsa";
import type {IFileSystemHandle} from "../fsa/types";
import type {IDirent, TDataOut} from "../node/types/misc";

export class FsaNodeDirent implements IDirent {
public constructor(public readonly name: TDataOut, protected readonly handle: IFileSystemHandle) {}

isDirectory(): boolean {
return this.handle instanceof NodeFileSystemDirectoryHandle;
}

isFile(): boolean {
return this.handle instanceof NodeFileSystemFileHandle;
}

isBlockDevice(): boolean {
return false;
}

isCharacterDevice(): boolean {
return false;
}

isSymbolicLink(): boolean {
return false;
}

public isFIFO(): boolean {
return false;
}

public isSocket(): boolean {
return false;
}
}
24 changes: 21 additions & 3 deletions src/fsa-to-node/FsaNodeFs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
flagsToNumber,
genRndStr6,
isFd,
isWin,
modeToNumber,
nullCheck,
pathToFilename,
Expand All @@ -25,6 +26,7 @@ import { strToEncoding } from '../encoding';
import { FsaToNodeConstants } from './constants';
import { bufferToEncoding } from '../volume';
import { FsaNodeFsOpenFile } from './FsaNodeFsOpenFile';
import {FsaNodeDirent} from './FsaNodeDirent';
import type { FsCallbackApi, FsPromisesApi } from '../node/types';
import type * as misc from '../node/types/misc';
import type * as opts from '../node/types/options';
Expand Down Expand Up @@ -296,9 +298,25 @@ export class FsaNodeFs implements FsCallbackApi {
folder.push(name);
this.getDir(folder, false, 'readdir')
.then(dir => (async () => {
const list: string[] = [];
for await (const key of dir.keys()) list.push(key);
return list;
if (options.withFileTypes) {
const list: misc.IDirent[] = [];
for await (const [name, handle] of dir.entries()) {
const dirent = new FsaNodeDirent(name, handle);
list.push(dirent);
}
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;
} else {
const list: string[] = [];
for await (const key of dir.keys()) list.push(key);
if (!isWin && options.encoding !== 'buffer') list.sort();
return list;
}
})())
.then(res => callback(null, res), err => callback(err));
};
Expand Down
13 changes: 13 additions & 0 deletions src/fsa-to-node/__tests__/FsaNodeFs.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IFsWithVolume, NestedDirectoryJSON, memfs } from '../..';
import { nodeToFsa } from '../../node-to-fsa';
import {IDirent} from '../../node/types/misc';
import { FsaNodeFs } from '../FsaNodeFs';

const setup = (json: NestedDirectoryJSON | null = null) => {
Expand Down Expand Up @@ -272,4 +273,16 @@ describe('.readdir()', () => {
expect(res.includes('empty-folder')).toBe(true);
expect(res.includes('f.html')).toBe(true);
});

test('can read directory contents with "withFileTypes" flag set', async () => {
const { fs, mfs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' });
const list = await fs.promises.readdir('/', {withFileTypes: true}) as IDirent[];
expect(list.length).toBe(3);
const names = list.map((item) => item.name);
expect(names).toStrictEqual(['empty-folder', 'f.html', 'folder']);
expect(list.find(item => item.name === 'folder')?.isDirectory()).toBe(true);
expect(list.find(item => item.name === 'empty-folder')?.isDirectory()).toBe(true);
expect(list.find(item => item.name === 'f.html')?.isFile()).toBe(true);
expect(list.find(item => item.name === 'f.html')?.isDirectory()).toBe(false);
});
});
2 changes: 2 additions & 0 deletions src/node/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import * as errors from '../internal/errors';
import type { FsCallbackApi } from './types';
import type * as misc from './types/misc';

export const isWin = process.platform === 'win32';

export function promisify(
fs: FsCallbackApi,
fn: string,
Expand Down
3 changes: 1 addition & 2 deletions src/volume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
flagsToNumber,
validateFd,
isFd,
isWin,
} from './node/util';
import type { PathLike, symlink } from 'fs';

Expand All @@ -59,8 +60,6 @@ const {

const { sep, relative, join, dirname } = pathModule.posix ? pathModule.posix : pathModule;

const isWin = process.platform === 'win32';

// ---------------------------------------- Types

// Node-style errors with a `code` property.
Expand Down

0 comments on commit 5d6f976

Please sign in to comment.