diff --git a/src/fsa-to-node/FsaNodeFs.ts b/src/fsa-to-node/FsaNodeFs.ts index a5952a91a..3d135e4d6 100644 --- a/src/fsa-to-node/FsaNodeFs.ts +++ b/src/fsa-to-node/FsaNodeFs.ts @@ -14,6 +14,7 @@ import { getWriteFileOptions, } from '../node/options'; import { + bufferToEncoding, createError, dataToBuffer, flagsToNumber, @@ -31,19 +32,19 @@ import { pathToLocation, testDirectoryIsWritable } from './util'; import { ERRSTR, MODE } from '../node/constants'; import { strToEncoding } from '../encoding'; import { FsaToNodeConstants } from './constants'; -import { bufferToEncoding } from '../volume'; import { FsaNodeFsOpenFile } from './FsaNodeFsOpenFile'; import { FsaNodeDirent } from './FsaNodeDirent'; import { FLAG } from '../consts/FLAG'; import { AMODE } from '../consts/AMODE'; import { constants } from '../constants'; import { FsaNodeStats } from './FsaNodeStats'; +import process from '../process'; +import { FsSynchronousApi } from '../node/types/FsSynchronousApi'; import type { FsCallbackApi, FsPromisesApi } from '../node/types'; import type * as misc from '../node/types/misc'; import type * as opts from '../node/types/options'; import type * as fsa from '../fsa/types'; import type { FsCommonObjects } from '../node/types/FsCommonObjects'; -import { FsSynchronousApi } from '../node/types/FsSynchronousApi'; const notSupported: (...args: any[]) => any = () => { throw new Error('Method not supported by the File System Access API.'); @@ -78,9 +79,13 @@ export class FsaNodeFs implements FsCallbackApi, FsSynchronousApi, FsCommonObjec */ private async getDir(path: string[], create: boolean, funcName?: string): Promise { let curr: fsa.IFileSystemDirectoryHandle = this.root; + const options: fsa.GetDirectoryHandleOptions = { create }; + try { - for (const name of path) curr = await curr.getDirectoryHandle(name, options); + for (const name of path) { + curr = await curr.getDirectoryHandle(name, options); + } } catch (error) { if (error && typeof error === 'object' && error.name === 'TypeMismatchError') throw createError('ENOTDIR', funcName, path.join(FsaToNodeConstants.Separator)); @@ -447,7 +452,7 @@ export class FsaNodeFs implements FsCallbackApi, FsSynchronousApi, FsCommonObjec const filename = pathToFilename(path); const [folder, name] = pathToLocation(filename); (async () => { - const node = await this.getFileOrDir(folder, name, 'access'); + const node = folder.length || name ? await this.getFileOrDir(folder, name, 'access') : this.root; const checkIfCanExecute = mode & AMODE.X_OK; if (checkIfCanExecute) throw createError('EACCESS', 'access', filename); const checkIfCanWrite = mode & AMODE.W_OK; @@ -508,7 +513,7 @@ export class FsaNodeFs implements FsCallbackApi, FsSynchronousApi, FsCommonObjec const [options, callback] = getReaddirOptsAndCb(a, b); const filename = pathToFilename(path); const [folder, name] = pathToLocation(filename); - folder.push(name); + if (name) folder.push(name); this.getDir(folder, false, 'readdir') .then(dir => (async () => { @@ -527,8 +532,11 @@ export class FsaNodeFs implements FsCallbackApi, FsSynchronousApi, FsCommonObjec 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; } })(), diff --git a/src/fsa-to-node/__tests__/util.test.ts b/src/fsa-to-node/__tests__/util.test.ts index 37d79da1b..7e065ed14 100644 --- a/src/fsa-to-node/__tests__/util.test.ts +++ b/src/fsa-to-node/__tests__/util.test.ts @@ -1,10 +1,14 @@ import { pathToLocation } from '../util'; describe('pathToLocation()', () => { - test('handles empty string', () => { + test('handles an empty string', () => { expect(pathToLocation('')).toStrictEqual([[], '']); }); + test('handles a single slash', () => { + expect(pathToLocation('/')).toStrictEqual([[], '']); + }); + test('no path, just filename', () => { expect(pathToLocation('scary.exe')).toStrictEqual([[], 'scary.exe']); }); diff --git a/src/fsa-to-node/index.ts b/src/fsa-to-node/index.ts new file mode 100644 index 000000000..9c9612c00 --- /dev/null +++ b/src/fsa-to-node/index.ts @@ -0,0 +1,8 @@ +import { FsaNodeFs } from './FsaNodeFs'; +import type { IFileSystemDirectoryHandle } from '../fsa/types'; + +export { FsaNodeFs }; + +export const fsaToNode = (root: IFileSystemDirectoryHandle) => { + return new FsaNodeFs(root); +}; diff --git a/src/node/util.ts b/src/node/util.ts index 60a792e42..935678a27 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -2,7 +2,7 @@ import { ERRSTR, FLAGS } from './constants'; import * as errors from '../internal/errors'; import type { FsCallbackApi } from './types'; import type * as misc from './types/misc'; -import { ENCODING_UTF8 } from '../encoding'; +import { ENCODING_UTF8, TEncodingExtended } from '../encoding'; import { bufferFrom } from '../internal/buffer'; export const isWin = process.platform === 'win32'; @@ -241,3 +241,8 @@ export const getWriteArgs = ( const cb = validateCallback(callback); return [fd, tipa === 'string', buf, offset, length!, position, cb]; }; + +export function bufferToEncoding(buffer: Buffer, encoding?: TEncodingExtended): misc.TDataOut { + if (!encoding || encoding === 'buffer') return buffer; + else return buffer.toString(encoding); +} diff --git a/src/volume.ts b/src/volume.ts index ada72ef1c..ec4a6f675 100644 --- a/src/volume.ts +++ b/src/volume.ts @@ -47,6 +47,7 @@ import { isWin, dataToBuffer, getWriteArgs, + bufferToEncoding, } from './node/util'; import type { PathLike, symlink } from 'fs'; @@ -201,11 +202,6 @@ export function dataToStr(data: TData, encoding: string = ENCODING_UTF8): string else return String(data); } -export function bufferToEncoding(buffer: Buffer, encoding?: TEncodingExtended): TDataOut { - if (!encoding || encoding === 'buffer') return buffer; - else return buffer.toString(encoding); -} - // converts Date or number to a fractional UNIX timestamp export function toUnixTimestamp(time) { // tslint:disable-next-line triple-equals