Skip to content

Commit 29946eb

Browse files
committed
fs: expose FileHandle class and add isFileHandle method
1 parent 8e41b8d commit 29946eb

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

lib/fs.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3372,4 +3372,15 @@ ObjectDefineProperties(fs, {
33723372
return promises;
33733373
},
33743374
},
3375+
FileHandle: {
3376+
__proto__: null,
3377+
configurable: true,
3378+
enumerable: true,
3379+
get() {
3380+
if (promises === null) {
3381+
promises = require('internal/fs/promises').exports;
3382+
}
3383+
return promises.FileHandle;
3384+
},
3385+
},
33753386
});

lib/internal/fs/promises.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class FileHandle extends EventEmitter {
162162
this[kClosePromise] = null;
163163
}
164164

165+
/**
166+
* Returns true if the given object is a FileHandle.
167+
* @param {any} value
168+
* @returns {boolean}
169+
*/
170+
static isFileHandle(value) {
171+
return value instanceof FileHandle;
172+
}
173+
165174
getAsyncId() {
166175
return this[kHandle].getAsyncId();
167176
}
@@ -1331,6 +1340,7 @@ module.exports = {
13311340
readFile,
13321341
watch: !isMacOS && !isWindows ? _watch : watch,
13331342
constants,
1343+
FileHandle,
13341344
},
13351345

13361346
FileHandle,
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
// Test for FileHandle class accessibility and isFileHandle method
6+
// This validates the implementation of GitHub issue #61637
7+
8+
const fs = require('fs');
9+
const fsPromises = require('fs/promises');
10+
const path = require('path');
11+
const assert = require('assert');
12+
const tmpdir = require('../common/tmpdir');
13+
const tmpDir = tmpdir.path;
14+
15+
tmpdir.refresh();
16+
17+
async function testFileHandleAccessibility() {
18+
// Test 1: FileHandle should be accessible from fs.FileHandle
19+
assert(fs.FileHandle !== undefined, 'fs.FileHandle should be defined');
20+
assert(typeof fs.FileHandle === 'function', 'fs.FileHandle should be a function/class');
21+
22+
// Test 2: FileHandle should be accessible from fs.promises.FileHandle
23+
assert(fsPromises.FileHandle !== undefined, 'fs.promises.FileHandle should be defined');
24+
assert(typeof fsPromises.FileHandle === 'function', 'fs.promises.FileHandle should be a function/class');
25+
26+
// Test 3: Both references should point to the same class
27+
assert.strictEqual(fs.FileHandle, fsPromises.FileHandle, 'fs.FileHandle and fs.promises.FileHandle should be the same');
28+
}
29+
30+
async function testIsFileHandleMethod() {
31+
// Test 4: FileHandle.isFileHandle should exist
32+
assert(typeof fs.FileHandle.isFileHandle === 'function', 'FileHandle.isFileHandle should be a function');
33+
34+
// Test 5: Test isFileHandle with actual FileHandle instance
35+
const testFilePath = path.join(tmpDir, 'test_filehandle.txt');
36+
await fsPromises.writeFile(testFilePath, 'test content');
37+
38+
const fileHandle = await fsPromises.open(testFilePath, 'r');
39+
try {
40+
assert(fs.FileHandle.isFileHandle(fileHandle) === true, 'isFileHandle should return true for FileHandle instance');
41+
} finally {
42+
await fileHandle.close();
43+
}
44+
45+
// Test 6: Test instanceof check
46+
const fileHandle2 = await fsPromises.open(testFilePath, 'r');
47+
try {
48+
assert(fileHandle2 instanceof fs.FileHandle, 'fileHandle should be instance of FileHandle');
49+
assert(fileHandle2 instanceof fsPromises.FileHandle, 'fileHandle should be instance of fs.promises.FileHandle');
50+
} finally {
51+
await fileHandle2.close();
52+
}
53+
54+
// Test 7: Test isFileHandle with non-FileHandle objects
55+
assert(fs.FileHandle.isFileHandle({}) === false, 'isFileHandle should return false for plain object');
56+
assert(fs.FileHandle.isFileHandle(null) === false, 'isFileHandle should return false for null');
57+
assert(fs.FileHandle.isFileHandle(undefined) === false, 'isFileHandle should return false for undefined');
58+
assert(fs.FileHandle.isFileHandle(5) === false, 'isFileHandle should return false for number (fd)');
59+
assert(fs.FileHandle.isFileHandle('path/to/file') === false, 'isFileHandle should return false for string');
60+
assert(fs.FileHandle.isFileHandle(Buffer.from('test')) === false, 'isFileHandle should return false for Buffer');
61+
62+
// Clean up
63+
await fsPromises.unlink(testFilePath);
64+
}
65+
66+
async function testFileHandleUsage() {
67+
// Test 8: Demonstrate practical usage - function accepting multiple input types
68+
const testFilePath = path.join(tmpDir, 'test_usage.txt');
69+
await fsPromises.writeFile(testFilePath, 'Hello FileHandle!');
70+
71+
async function flexibleReadFile(input) {
72+
if (fs.FileHandle.isFileHandle(input)) {
73+
// Input is already a FileHandle
74+
return await input.readFile({ encoding: 'utf8' });
75+
} else if (typeof input === 'string') {
76+
// Input is a file path
77+
const fh = await fsPromises.open(input, 'r');
78+
try {
79+
return await fh.readFile({ encoding: 'utf8' });
80+
} finally {
81+
await fh.close();
82+
}
83+
} else {
84+
throw new TypeError('Input must be a file path or FileHandle');
85+
}
86+
}
87+
88+
// Test with file path
89+
const content1 = await flexibleReadFile(testFilePath);
90+
assert.strictEqual(content1, 'Hello FileHandle!');
91+
92+
// Test with FileHandle
93+
const fh = await fsPromises.open(testFilePath, 'r');
94+
try {
95+
const content2 = await flexibleReadFile(fh);
96+
assert.strictEqual(content2, 'Hello FileHandle!');
97+
} finally {
98+
await fh.close();
99+
}
100+
101+
// Clean up
102+
await fsPromises.unlink(testFilePath);
103+
}
104+
105+
(async () => {
106+
await testFileHandleAccessibility();
107+
await testIsFileHandleMethod();
108+
await testFileHandleUsage();
109+
})().then(common.mustCall());

0 commit comments

Comments
 (0)