Skip to content

Commit 22c3842

Browse files
committed
vfs: address code review feedback from @jasnell
- Use undefined instead of null for lazy-loaded SEAProvider - Add validateBoolean for moduleHooks and virtualCwd options - Use template literal for path concatenation - Convert VirtualReadStream to use private class fields - Cache DateNow() result in MemoryEntry constructor Addresses review comments #18, #19, #21, #23, #24, #29.
1 parent 3388e9d commit 22c3842

File tree

4 files changed

+53
-42
lines changed

4 files changed

+53
-42
lines changed

lib/internal/vfs/file_system.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
ERR_INVALID_STATE,
1111
},
1212
} = require('internal/errors');
13+
const { validateBoolean } = require('internal/validators');
1314
const { MemoryProvider } = require('internal/vfs/providers/memory');
1415
const {
1516
normalizePath,
@@ -81,6 +82,14 @@ class VirtualFileSystem {
8182
}
8283
}
8384

85+
// Validate boolean options
86+
if (options.moduleHooks !== undefined) {
87+
validateBoolean(options.moduleHooks, 'options.moduleHooks');
88+
}
89+
if (options.virtualCwd !== undefined) {
90+
validateBoolean(options.virtualCwd, 'options.virtualCwd');
91+
}
92+
8493
this[kProvider] = provider ?? new MemoryProvider();
8594
this[kMountPoint] = null;
8695
this[kMounted] = false;
@@ -181,7 +190,7 @@ class VirtualFileSystem {
181190

182191
// If virtual cwd is enabled and set, resolve relative to it
183192
if (this[kVirtualCwdEnabled] && this[kVirtualCwd] !== null) {
184-
const resolved = this[kVirtualCwd] + '/' + inputPath;
193+
const resolved = `${this[kVirtualCwd]}/${inputPath}`;
185194
return normalizePath(resolved);
186195
}
187196

lib/internal/vfs/providers/memory.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ class MemoryEntry {
6363
this.children = null; // For directories
6464
this.populate = null; // For directories - lazy population callback
6565
this.populated = true; // For directories - has populate been called?
66-
this.mtime = DateNow();
67-
this.ctime = DateNow();
68-
this.birthtime = DateNow();
66+
const now = DateNow();
67+
this.mtime = now;
68+
this.ctime = now;
69+
this.birthtime = now;
6970
}
7071

7172
/**

lib/internal/vfs/streams.js

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ const { createEBADF } = require('internal/vfs/errors');
1111
* A readable stream for virtual files.
1212
*/
1313
class VirtualReadStream extends Readable {
14+
#vfs;
15+
#path;
16+
#fd = null;
17+
#end;
18+
#pos;
19+
#content = null;
20+
#destroyed = false;
21+
#autoClose;
22+
1423
/**
1524
* @param {VirtualFileSystem} vfs The VFS instance
1625
* @param {string} filePath The path to the file
@@ -27,38 +36,33 @@ class VirtualReadStream extends Readable {
2736

2837
super({ ...streamOptions, highWaterMark, encoding });
2938

30-
this._vfs = vfs;
31-
this._path = filePath;
32-
this._fd = null;
33-
this._start = start;
34-
this._end = end;
35-
this._pos = start;
36-
this._content = null;
37-
this._destroyed = false;
38-
this._autoClose = options.autoClose !== false;
39+
this.#vfs = vfs;
40+
this.#path = filePath;
41+
this.#end = end;
42+
this.#pos = start;
43+
this.#autoClose = options.autoClose !== false;
3944

4045
// Open the file on next tick so listeners can be attached
41-
process.nextTick(() => this._openFile());
46+
process.nextTick(() => this.#openFile());
4247
}
4348

4449
/**
4550
* Gets the file path.
4651
* @returns {string}
4752
*/
4853
get path() {
49-
return this._path;
54+
return this.#path;
5055
}
5156

5257
/**
5358
* Opens the virtual file.
5459
* Events are emitted synchronously within this method, which runs
5560
* asynchronously via process.nextTick - matching real fs behavior.
56-
* @private
5761
*/
58-
_openFile() {
62+
#openFile() {
5963
try {
60-
this._fd = this._vfs.openSync(this._path);
61-
this.emit('open', this._fd);
64+
this.#fd = this.#vfs.openSync(this.#path);
65+
this.emit('open', this.#fd);
6266
this.emit('ready');
6367
} catch (err) {
6468
this.destroy(err);
@@ -68,23 +72,22 @@ class VirtualReadStream extends Readable {
6872
/**
6973
* Implements the readable _read method.
7074
* @param {number} size Number of bytes to read
71-
* @private
7275
*/
7376
_read(size) {
74-
if (this._destroyed || this._fd === null) {
77+
if (this.#destroyed || this.#fd === null) {
7578
return;
7679
}
7780

7881
// Load content on first read (lazy loading)
79-
if (this._content === null) {
82+
if (this.#content === null) {
8083
try {
81-
const vfd = require('internal/vfs/fd').getVirtualFd(this._fd);
84+
const vfd = require('internal/vfs/fd').getVirtualFd(this.#fd);
8285
if (!vfd) {
8386
this.destroy(createEBADF('read'));
8487
return;
8588
}
8689
// Use the file handle's readFileSync to get content
87-
this._content = vfd.entry.readFileSync();
90+
this.#content = vfd.entry.readFileSync();
8891
} catch (err) {
8992
this.destroy(err);
9093
return;
@@ -93,53 +96,51 @@ class VirtualReadStream extends Readable {
9396

9497
// Calculate how much to read
9598
// Note: end is inclusive, so we use end + 1 for the upper bound
96-
const endPos = this._end === Infinity ? this._content.length : this._end + 1;
97-
const remaining = MathMin(endPos, this._content.length) - this._pos;
99+
const endPos = this.#end === Infinity ? this.#content.length : this.#end + 1;
100+
const remaining = MathMin(endPos, this.#content.length) - this.#pos;
98101
if (remaining <= 0) {
99102
this.push(null);
100-
// Note: _close() will be called by _destroy() when autoClose is true
103+
// Note: #close() will be called by _destroy() when autoClose is true
101104
return;
102105
}
103106

104107
const bytesToRead = MathMin(size, remaining);
105-
const chunk = this._content.subarray(this._pos, this._pos + bytesToRead);
106-
this._pos += bytesToRead;
108+
const chunk = this.#content.subarray(this.#pos, this.#pos + bytesToRead);
109+
this.#pos += bytesToRead;
107110

108111
this.push(chunk);
109112

110113
// Check if we've reached the end
111-
if (this._pos >= endPos || this._pos >= this._content.length) {
114+
if (this.#pos >= endPos || this.#pos >= this.#content.length) {
112115
this.push(null);
113-
// Note: _close() will be called by _destroy() when autoClose is true
116+
// Note: #close() will be called by _destroy() when autoClose is true
114117
}
115118
}
116119

117120
/**
118121
* Closes the file descriptor.
119122
* Note: Does not emit 'close' - the base Readable class handles that.
120-
* @private
121123
*/
122-
_close() {
123-
if (this._fd !== null) {
124+
#close() {
125+
if (this.#fd !== null) {
124126
try {
125-
this._vfs.closeSync(this._fd);
127+
this.#vfs.closeSync(this.#fd);
126128
} catch {
127129
// Ignore close errors
128130
}
129-
this._fd = null;
131+
this.#fd = null;
130132
}
131133
}
132134

133135
/**
134136
* Implements the readable _destroy method.
135137
* @param {Error|null} err The error
136138
* @param {Function} callback Callback
137-
* @private
138139
*/
139140
_destroy(err, callback) {
140-
this._destroyed = true;
141-
if (this._autoClose) {
142-
this._close();
141+
this.#destroyed = true;
142+
if (this.#autoClose) {
143+
this.#close();
143144
}
144145
callback(err);
145146
}

lib/vfs.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ const { MemoryProvider } = require('internal/vfs/providers/memory');
1111
const { RealFSProvider } = require('internal/vfs/providers/real');
1212

1313
// SEAProvider is lazy-loaded to avoid loading SEA bindings when not needed
14-
let SEAProvider = null;
14+
let SEAProvider;
1515

1616
function getSEAProvider() {
17-
if (SEAProvider === null) {
17+
if (SEAProvider === undefined) {
1818
try {
1919
SEAProvider = require('internal/vfs/providers/sea').SEAProvider;
2020
} catch {

0 commit comments

Comments
 (0)