Skip to content

Commit 4390275

Browse files
Copilotstreamich
andcommitted
Refactor realpath to use constructor pattern with native properties
Co-authored-by: streamich <9773803+streamich@users.noreply.github.com>
1 parent 70172d9 commit 4390275

9 files changed

Lines changed: 66 additions & 83 deletions

File tree

src/__tests__/fs-native-realpath.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ describe('fs.realpath vs fs.realpath.native comparison', () => {
133133

134134
const regularPath = testFs.realpathSync('/symlink/file.txt');
135135
const nativePath = testFs.realpathSync.native('/symlink/file.txt');
136-
136+
137137
expect(regularPath).toBe(nativePath);
138138
expect(regularPath).toBe('/dir/file.txt');
139139
});
140-
});
140+
});

src/__tests__/volume/realpathNative.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { create } from '../util';
22

3-
describe('.realpathNative(...)', () => {
3+
describe('.realpath.native(...)', () => {
44
it('works with async callback', done => {
55
const vol = create({});
66
vol.mkdirSync('/a');
77
vol.mkdirSync('/c');
88
vol.writeFileSync('/c/index.js', 'alert(123);');
99
vol.symlinkSync('/c', '/a/b');
1010

11-
vol.realpathNative('/a/b/index.js', (err, path) => {
11+
vol.realpath.native('/a/b/index.js', (err, path) => {
1212
expect(err).toBe(null);
1313
expect(path).toBe('/c/index.js');
1414
done();
@@ -22,7 +22,7 @@ describe('.realpathNative(...)', () => {
2222
vol.writeFileSync('/c/index.js', 'alert(123);');
2323
vol.symlinkSync('/c', '/a/b');
2424

25-
vol.realpathNative('/a/b/index.js', 'utf8', (err, path) => {
25+
vol.realpath.native('/a/b/index.js', 'utf8', (err, path) => {
2626
expect(err).toBe(null);
2727
expect(path).toBe('/c/index.js');
2828
done();
@@ -36,7 +36,7 @@ describe('.realpathNative(...)', () => {
3636
vol.writeFileSync('/c/index.js', 'alert(123);');
3737
vol.symlinkSync('/c', '/a/b');
3838

39-
vol.realpathNative('/a/b/index.js', { encoding: 'utf8' }, (err, path) => {
39+
vol.realpath.native('/a/b/index.js', { encoding: 'utf8' }, (err, path) => {
4040
expect(err).toBe(null);
4141
expect(path).toBe('/c/index.js');
4242
done();
@@ -45,7 +45,7 @@ describe('.realpathNative(...)', () => {
4545

4646
it('returns the root correctly', done => {
4747
const vol = create({ './a': 'a' });
48-
vol.realpathNative('/', (err, path) => {
48+
vol.realpath.native('/', (err, path) => {
4949
expect(err).toBe(null);
5050
expect(path).toBe('/');
5151
done();
@@ -54,7 +54,7 @@ describe('.realpathNative(...)', () => {
5454

5555
it('handles errors correctly', done => {
5656
const vol = create({});
57-
vol.realpathNative('/nonexistent', (err, path) => {
57+
vol.realpath.native('/nonexistent', (err, path) => {
5858
expect(err).toBeTruthy();
5959
expect(err?.code).toBe('ENOENT');
6060
expect(path).toBeUndefined();
@@ -63,15 +63,15 @@ describe('.realpathNative(...)', () => {
6363
});
6464
});
6565

66-
describe('.realpathNativeSync(...)', () => {
66+
describe('.realpathSync.native(...)', () => {
6767
it('works with symlinks', () => {
6868
const vol = create({});
6969
vol.mkdirSync('/a');
7070
vol.mkdirSync('/c');
7171
vol.writeFileSync('/c/index.js', 'alert(123);');
7272
vol.symlinkSync('/c', '/a/b');
7373

74-
const path = vol.realpathNativeSync('/a/b/index.js');
74+
const path = vol.realpathSync.native('/a/b/index.js');
7575
expect(path).toBe('/c/index.js');
7676
});
7777

@@ -82,7 +82,7 @@ describe('.realpathNativeSync(...)', () => {
8282
vol.writeFileSync('/c/index.js', 'alert(123);');
8383
vol.symlinkSync('/c', '/a/b');
8484

85-
const path = vol.realpathNativeSync('/a/b/index.js', 'utf8');
85+
const path = vol.realpathSync.native('/a/b/index.js', 'utf8');
8686
expect(path).toBe('/c/index.js');
8787
});
8888

@@ -93,35 +93,35 @@ describe('.realpathNativeSync(...)', () => {
9393
vol.writeFileSync('/c/index.js', 'alert(123);');
9494
vol.symlinkSync('/c', '/a/b');
9595

96-
const path = vol.realpathNativeSync('/a/b/index.js', { encoding: 'utf8' });
96+
const path = vol.realpathSync.native('/a/b/index.js', { encoding: 'utf8' });
9797
expect(path).toBe('/c/index.js');
9898
});
9999

100100
it('returns the root correctly', () => {
101101
const vol = create({ './a': 'a' });
102-
expect(vol.realpathNativeSync('/')).toBe('/');
102+
expect(vol.realpathSync.native('/')).toBe('/');
103103
});
104104

105105
it('throws EACCES when the containing directory does not have sufficient permissions', () => {
106106
const vol = create({ '/foo/bar': 'bar' });
107107
vol.chmodSync('/foo', 0o666); // rw
108108
expect(() => {
109-
vol.realpathNativeSync('/foo/bar');
109+
vol.realpathSync.native('/foo/bar');
110110
}).toThrow(/EACCES/);
111111
});
112112

113113
it('throws EACCES when an intermediate directory does not have sufficient permissions', () => {
114114
const vol = create({ '/foo/bar': 'bar' });
115115
vol.chmodSync('/', 0o666); // rw
116116
expect(() => {
117-
vol.realpathNativeSync('/foo/bar');
117+
vol.realpathSync.native('/foo/bar');
118118
}).toThrow(/EACCES/);
119119
});
120120

121121
it('throws ENOENT for non-existent paths', () => {
122122
const vol = create({});
123123
expect(() => {
124-
vol.realpathNativeSync('/nonexistent');
124+
vol.realpathSync.native('/nonexistent');
125125
}).toThrow(/ENOENT/);
126126
});
127-
});
127+
});

src/fsa-to-node/FsaNodeFs.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -298,17 +298,6 @@ export class FsaNodeFs extends FsaNodeCore implements FsCallbackApi, FsSynchrono
298298
callback(null, strToEncoding(pathFilename, opts.encoding));
299299
};
300300

301-
public readonly realpathNative: FsCallbackApi['realpathNative'] = (
302-
path: misc.PathLike,
303-
a: misc.TCallback<misc.TDataOut> | opts.IRealpathOptions | string,
304-
b?: misc.TCallback<misc.TDataOut>,
305-
): void => {
306-
const [opts, callback] = optHelpers.getRealpathOptsAndCb(a, b);
307-
let pathFilename = util.pathToFilename(path);
308-
if (pathFilename[0] !== FsaToNodeConstants.Separator) pathFilename = FsaToNodeConstants.Separator + pathFilename;
309-
callback(null, strToEncoding(pathFilename, opts.encoding));
310-
};
311-
312301
public readonly stat: FsCallbackApi['stat'] = (
313302
path: misc.PathLike,
314303
a: misc.TCallback<misc.IStats> | opts.IStatOptions,
@@ -1025,16 +1014,6 @@ export class FsaNodeFs extends FsaNodeCore implements FsCallbackApi, FsSynchrono
10251014
return strToEncoding(filename, encoding);
10261015
};
10271016

1028-
public readonly realpathNativeSync: FsSynchronousApi['realpathNativeSync'] = (
1029-
path: misc.PathLike,
1030-
options?: opts.IRealpathOptions | string,
1031-
): misc.TDataOut => {
1032-
let filename = util.pathToFilename(path);
1033-
const { encoding } = optHelpers.getRealpathOptions(options);
1034-
if (filename[0] !== FsaToNodeConstants.Separator) filename = FsaToNodeConstants.Separator + filename;
1035-
return strToEncoding(filename, encoding);
1036-
};
1037-
10381017
public readonly readSync: FsSynchronousApi['readSync'] = (
10391018
fd: number,
10401019
buffer: Buffer | ArrayBufferView | DataView,

src/index.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,6 @@ export interface IFs extends Volume {
3131
WriteStream: new (...args) => IWriteStream;
3232
promises: FsPromisesApi;
3333
_toUnixTimestamp;
34-
35-
// Override realpath and realpathSync to include native properties
36-
realpath: Volume['realpath'] & {
37-
native: Volume['realpathNative'];
38-
};
39-
realpathSync: Volume['realpathSync'] & {
40-
native: Volume['realpathNativeSync'];
41-
};
4234
}
4335

4436
export function createFsFromVolume(vol: Volume): IFs {
@@ -48,14 +40,6 @@ export function createFsFromVolume(vol: Volume): IFs {
4840
for (const method of fsSynchronousApiList) if (typeof vol[method] === 'function') fs[method] = vol[method].bind(vol);
4941
for (const method of fsCallbackApiList) if (typeof vol[method] === 'function') fs[method] = vol[method].bind(vol);
5042

51-
// Bind native realpath methods as properties
52-
if (typeof vol.realpathNative === 'function') {
53-
(fs.realpath as any).native = vol.realpathNative.bind(vol);
54-
}
55-
if (typeof vol.realpathNativeSync === 'function') {
56-
(fs.realpathSync as any).native = vol.realpathNativeSync.bind(vol);
57-
}
58-
5943
fs.StatWatcher = vol.StatWatcher;
6044
fs.FSWatcher = vol.FSWatcher;
6145
fs.WriteStream = vol.WriteStream;

src/node/lists/fsCallbackApiList.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export const fsCallbackApiList: Array<keyof FsCallbackApi> = [
3131
'readFile',
3232
'readlink',
3333
'realpath',
34-
'realpathNative',
3534
'rename',
3635
'rm',
3736
'rmdir',

src/node/lists/fsSynchronousApiList.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export const fsSynchronousApiList: Array<keyof FsSynchronousApi> = [
2929
'readSync',
3030
'readvSync',
3131
'realpathSync',
32-
'realpathNativeSync',
3332
'renameSync',
3433
'rmdirSync',
3534
'rmSync',

src/node/types/FsCallbackApi.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ export interface FsCallbackApi {
8282
): void;
8383
realpath(path: misc.PathLike, callback: misc.TCallback<misc.TDataOut>);
8484
realpath(path: misc.PathLike, options: opts.IRealpathOptions | string, callback: misc.TCallback<misc.TDataOut>);
85-
realpathNative(path: misc.PathLike, callback: misc.TCallback<misc.TDataOut>);
86-
realpathNative(path: misc.PathLike, options: opts.IRealpathOptions | string, callback: misc.TCallback<misc.TDataOut>);
8785
rename(oldPath: misc.PathLike, newPath: misc.PathLike, callback: misc.TCallback<void>): void;
8886
rmdir(path: misc.PathLike, callback: misc.TCallback<void>);
8987
rmdir(path: misc.PathLike, options: opts.IRmdirOptions, callback: misc.TCallback<void>);

src/node/types/FsSynchronousApi.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export interface FsSynchronousApi {
4848
readFileSync(file: misc.TFileId, options?: opts.IReadFileOptions | string): misc.TDataOut;
4949
readvSync(fd: number, buffers: ArrayBufferView[], position?: number | null): number;
5050
realpathSync(path: misc.PathLike, options?: opts.IRealpathOptions | string): misc.TDataOut;
51-
realpathNativeSync(path: misc.PathLike, options?: opts.IRealpathOptions | string): misc.TDataOut;
5251
renameSync(oldPath: misc.PathLike, newPath: misc.PathLike): void;
5352
rmdirSync(path: misc.PathLike, options?: opts.IRmdirOptions): void;
5453
rmSync(path: misc.PathLike, options?: opts.IRmOptions): void;

src/volume.ts

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,21 @@ export class Volume implements FsCallbackApi, FsSynchronousApi {
290290
WriteStream: new (...args) => IWriteStream;
291291
FSWatcher: new () => FSWatcher;
292292

293+
// realpath function properties with native variants
294+
realpath: {
295+
(path: PathLike, callback: TCallback<TDataOut>): void;
296+
(path: PathLike, options: opts.IRealpathOptions | string, callback: TCallback<TDataOut>): void;
297+
native: {
298+
(path: PathLike, callback: TCallback<TDataOut>): void;
299+
(path: PathLike, options: opts.IRealpathOptions | string, callback: TCallback<TDataOut>): void;
300+
};
301+
};
302+
303+
realpathSync: {
304+
(path: PathLike, options?: opts.IRealpathOptions | string): TDataOut;
305+
native: (path: PathLike, options?: opts.IRealpathOptions | string) => TDataOut;
306+
};
307+
293308
props: {
294309
Node: new (...args) => Node;
295310
Link: new (...args) => Link;
@@ -344,6 +359,40 @@ export class Volume implements FsCallbackApi, FsSynchronousApi {
344359
root.getNode().nlink++;
345360

346361
this.root = root;
362+
363+
// Set up realpath as function properties with native variants
364+
const realpathImpl = (
365+
path: PathLike,
366+
a: TCallback<TDataOut> | opts.IRealpathOptions | string,
367+
b?: TCallback<TDataOut>,
368+
) => {
369+
const [opts, callback] = getRealpathOptsAndCb(a, b);
370+
const pathFilename = pathToFilename(path);
371+
self.wrapAsync(self.realpathBase, [pathFilename, opts.encoding], callback);
372+
};
373+
374+
const realpathNativeImpl = (
375+
path: PathLike,
376+
a: TCallback<TDataOut> | opts.IRealpathOptions | string,
377+
b?: TCallback<TDataOut>,
378+
) => {
379+
const [opts, callback] = getRealpathOptsAndCb(a, b);
380+
const pathFilename = pathToFilename(path);
381+
self.wrapAsync(self.realpathBase, [pathFilename, opts.encoding], callback);
382+
};
383+
384+
const realpathSyncImpl = (path: PathLike, options?: opts.IRealpathOptions | string): TDataOut => {
385+
return self.realpathBase(pathToFilename(path), getRealpathOptions(options).encoding);
386+
};
387+
388+
const realpathSyncNativeImpl = (path: PathLike, options?: opts.IRealpathOptions | string): TDataOut => {
389+
return self.realpathBase(pathToFilename(path), getRealpathOptions(options).encoding);
390+
};
391+
392+
this.realpath = realpathImpl as any;
393+
this.realpath.native = realpathNativeImpl as any;
394+
this.realpathSync = realpathSyncImpl as any;
395+
this.realpathSync.native = realpathSyncNativeImpl as any;
347396
}
348397

349398
createLink(): Link;
@@ -1506,30 +1555,6 @@ export class Volume implements FsCallbackApi, FsSynchronousApi {
15061555
return strToEncoding(realLink.getPath() || '/', encoding);
15071556
}
15081557

1509-
realpathSync(path: PathLike, options?: opts.IRealpathOptions | string): TDataOut {
1510-
return this.realpathBase(pathToFilename(path), getRealpathOptions(options).encoding);
1511-
}
1512-
1513-
realpath(path: PathLike, callback: TCallback<TDataOut>);
1514-
realpath(path: PathLike, options: opts.IRealpathOptions | string, callback: TCallback<TDataOut>);
1515-
realpath(path: PathLike, a: TCallback<TDataOut> | opts.IRealpathOptions | string, b?: TCallback<TDataOut>) {
1516-
const [opts, callback] = getRealpathOptsAndCb(a, b);
1517-
const pathFilename = pathToFilename(path);
1518-
this.wrapAsync(this.realpathBase, [pathFilename, opts.encoding], callback);
1519-
}
1520-
1521-
realpathNative(path: PathLike, callback: TCallback<TDataOut>);
1522-
realpathNative(path: PathLike, options: opts.IRealpathOptions | string, callback: TCallback<TDataOut>);
1523-
realpathNative(path: PathLike, a: TCallback<TDataOut> | opts.IRealpathOptions | string, b?: TCallback<TDataOut>) {
1524-
const [opts, callback] = getRealpathOptsAndCb(a, b);
1525-
const pathFilename = pathToFilename(path);
1526-
this.wrapAsync(this.realpathBase, [pathFilename, opts.encoding], callback);
1527-
}
1528-
1529-
realpathNativeSync(path: PathLike, options?: opts.IRealpathOptions | string): TDataOut {
1530-
return this.realpathBase(pathToFilename(path), getRealpathOptions(options).encoding);
1531-
}
1532-
15331558
private lstatBase(filename: string, bigint: false, throwIfNoEntry: true): Stats<number>;
15341559
private lstatBase(filename: string, bigint: true, throwIfNoEntry: true): Stats<bigint>;
15351560
private lstatBase(filename: string, bigint: true, throwIfNoEntry: false): Stats<bigint> | undefined;

0 commit comments

Comments
 (0)