From 458a7b285cc0403563673d90557908d4057ed39f Mon Sep 17 00:00:00 2001 From: streamich Date: Sat, 17 Jun 2023 20:08:49 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20implement=20realpath()?= =?UTF-8?q?=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fsa-to-node/FsaNodeFs.ts | 27 ++++++++------------- src/fsa-to-node/__tests__/FsaNodeFs.test.ts | 8 ++++++ src/node/options.ts | 4 +++ src/volume.ts | 16 ++++-------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/fsa-to-node/FsaNodeFs.ts b/src/fsa-to-node/FsaNodeFs.ts index 396c6ade9..718651f80 100644 --- a/src/fsa-to-node/FsaNodeFs.ts +++ b/src/fsa-to-node/FsaNodeFs.ts @@ -9,6 +9,7 @@ import { optsAndCbGenerator, getAppendFileOptsAndCb, getStatOptsAndCb, + getRealpathOptsAndCb, } from '../node/options'; import { createError, @@ -279,26 +280,17 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects { ); }; - symlink(target: misc.PathLike, path: misc.PathLike, callback: misc.TCallback); - symlink(target: misc.PathLike, path: misc.PathLike, type: misc.symlink.Type, callback: misc.TCallback); - symlink( - target: misc.PathLike, - path: misc.PathLike, - a: misc.symlink.Type | misc.TCallback, - b?: misc.TCallback, - ) { - throw new Error('Not implemented'); - } - - realpath(path: misc.PathLike, callback: misc.TCallback); - realpath(path: misc.PathLike, options: opts.IRealpathOptions | string, callback: misc.TCallback); - realpath( + public readonly realpath: FsCallbackApi['realpath'] = ( path: misc.PathLike, a: misc.TCallback | opts.IRealpathOptions | string, b?: misc.TCallback, - ) { - throw new Error('Not implemented'); - } + ): void => { + const [opts, callback] = getRealpathOptsAndCb(a, b); + let pathFilename = pathToFilename(path); + if (pathFilename[0] !== FsaToNodeConstants.Separator) + pathFilename = FsaToNodeConstants.Separator + pathFilename; + callback(null, strToEncoding(pathFilename, opts.encoding)); + }; public readonly lstat: FsCallbackApi['lstat'] = ( path: misc.PathLike, @@ -702,6 +694,7 @@ export class FsaNodeFs implements FsCallbackApi, FsCommonObjects { callback(null); } + public readonly symlink: FsCallbackApi['symlink'] = notSupported; public readonly link: FsCallbackApi['link'] = notSupported; public readonly watchFile: FsCallbackApi['watchFile'] = notSupported; public readonly unwatchFile: FsCallbackApi['unwatchFile'] = notSupported; diff --git a/src/fsa-to-node/__tests__/FsaNodeFs.test.ts b/src/fsa-to-node/__tests__/FsaNodeFs.test.ts index 74b8f8fce..d5be9f47f 100644 --- a/src/fsa-to-node/__tests__/FsaNodeFs.test.ts +++ b/src/fsa-to-node/__tests__/FsaNodeFs.test.ts @@ -539,3 +539,11 @@ describe('.fstat()', () => { expect(stats.isFile()).toBe(true); }); }); + +describe('.realpath()', () => { + test('returns file path', async () => { + const { fs } = setup({ folder: { file: 'test' }, 'empty-folder': null, 'f.html': 'test' }); + const path = await fs.promises.realpath('folder/file'); + expect(path).toBe('/folder/file'); + }); +}); diff --git a/src/node/options.ts b/src/node/options.ts index 5578ce74a..4ee1fc189 100644 --- a/src/node/options.ts +++ b/src/node/options.ts @@ -98,3 +98,7 @@ export const getStatOptsAndCb: ( callback?: misc.TCallback, ) => [opts.IStatOptions, misc.TCallback] = (options, callback?) => typeof options === 'function' ? [getStatOptions(), options] : [getStatOptions(options), validateCallback(callback)]; + +const realpathDefaults: opts.IReadFileOptions = optsDefaults; +export const getRealpathOptions = optsGenerator(realpathDefaults); +export const getRealpathOptsAndCb = optsAndCbGenerator(getRealpathOptions); diff --git a/src/volume.ts b/src/volume.ts index 06db2d84c..3179bad06 100644 --- a/src/volume.ts +++ b/src/volume.ts @@ -31,6 +31,8 @@ import { getAppendFileOpts, getStatOptsAndCb, getStatOptions, + getRealpathOptsAndCb, + getRealpathOptions, } from './node/options'; import { validateCallback, @@ -128,14 +130,6 @@ const getWriteFileOptions = optsGenerator(writeFileDefaults); // Options for `fs.appendFile` and `fs.appendFileSync` export interface IAppendFileOptions extends opts.IFileOptions {} -// Options for `fs.realpath` and `fs.realpathSync` -export interface IRealpathOptions { - encoding?: TEncodingExtended; -} -const realpathDefaults: opts.IReadFileOptions = optsDefaults; -const getRealpathOptions = optsGenerator(realpathDefaults); -const getRealpathOptsAndCb = optsAndCbGenerator(getRealpathOptions); - // Options for `fs.watchFile` export interface IWatchFileOptions { persistent?: boolean; @@ -1201,13 +1195,13 @@ export class Volume { return strToEncoding(realLink.getPath() || '/', encoding); } - realpathSync(path: PathLike, options?: IRealpathOptions | string): TDataOut { + realpathSync(path: PathLike, options?: opts.IRealpathOptions | string): TDataOut { return this.realpathBase(pathToFilename(path), getRealpathOptions(options).encoding); } realpath(path: PathLike, callback: TCallback); - realpath(path: PathLike, options: IRealpathOptions | string, callback: TCallback); - realpath(path: PathLike, a: TCallback | IRealpathOptions | string, b?: TCallback) { + realpath(path: PathLike, options: opts.IRealpathOptions | string, callback: TCallback); + realpath(path: PathLike, a: TCallback | opts.IRealpathOptions | string, b?: TCallback) { const [opts, callback] = getRealpathOptsAndCb(a, b); const pathFilename = pathToFilename(path); this.wrapAsync(this.realpathBase, [pathFilename, opts.encoding], callback);