Skip to content

Commit b70fb8d

Browse files
robertsLandoclaude
andcommitted
fix: add fs.promises and fs.access/accessSync patches to module hooks
The VFS module hooks only patched synchronous fs methods, leaving async operations (fs.promises.*, fs.access) unpatched. This caused ENOENT errors when application code used: - `await fs.promises.access(path)` (e.g., file existence checks) - `await fs.promises.readFile(path)` (e.g., loading templates) - `await fs.promises.stat(path)`, `lstat`, `readdir`, `readlink`, `realpath` - `fs.accessSync(path)` or `fs.access(path, callback)` Since `require('fs/promises')` returns the same object as `fs.promises`, patching directly on `fs.promises` covers both import patterns. Fixes: #8 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2b9da9b commit b70fb8d

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

lib/module_hooks.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,124 @@ function installFsPatches() {
901901
return originalExistsSync.call(fs, path);
902902
};
903903

904+
// --- Async callback patches (fs.access, fs.accessSync) ---
905+
906+
const originalAccessSync = fs.accessSync;
907+
fs.accessSync = function accessSync(path, mode) {
908+
if (typeof path === 'string') {
909+
const vfsResult = findVFSForExists(path);
910+
if (vfsResult !== null) {
911+
if (!vfsResult.exists) {
912+
throw createENOENT('access', path);
913+
}
914+
return;
915+
}
916+
}
917+
return originalAccessSync.call(fs, path, mode);
918+
};
919+
920+
const originalAccess = fs.access;
921+
fs.access = function access(path, mode, callback) {
922+
if (typeof mode === 'function') {
923+
callback = mode;
924+
mode = fs.constants.F_OK;
925+
}
926+
if (typeof path === 'string') {
927+
const vfsResult = findVFSForExists(path);
928+
if (vfsResult !== null) {
929+
const err = vfsResult.exists ? null : createENOENT('access', path);
930+
if (callback) process.nextTick(callback, err);
931+
return;
932+
}
933+
}
934+
return originalAccess.call(fs, path, mode, callback);
935+
};
936+
937+
// --- fs.promises patches ---
938+
// Patched directly on the shared object so require('fs/promises') also
939+
// picks up the changes (it returns the same reference as fs.promises).
940+
// Fixes: https://github.com/platformatic/vfs/issues/8
941+
942+
const origPAccess = fs.promises.access;
943+
fs.promises.access = async function access(path, mode) {
944+
if (typeof path === 'string') {
945+
const vfsResult = findVFSForExists(path);
946+
if (vfsResult !== null) {
947+
if (!vfsResult.exists) {
948+
throw createENOENT('access', path);
949+
}
950+
return;
951+
}
952+
}
953+
return origPAccess.call(fs.promises, path, mode);
954+
};
955+
956+
const origPReadFile = fs.promises.readFile;
957+
fs.promises.readFile = async function readFile(path, options) {
958+
if (typeof path === 'string') {
959+
const vfsResult = findVFSForRead(path, options);
960+
if (vfsResult !== null) {
961+
return vfsResult.content;
962+
}
963+
}
964+
return origPReadFile.call(fs.promises, path, options);
965+
};
966+
967+
const origPStat = fs.promises.stat;
968+
fs.promises.stat = async function stat(path, options) {
969+
if (typeof path === 'string') {
970+
const vfsResult = findVFSForFsStat(path);
971+
if (vfsResult !== null) {
972+
return vfsResult.stats;
973+
}
974+
}
975+
return origPStat.call(fs.promises, path, options);
976+
};
977+
978+
const origPLstat = fs.promises.lstat;
979+
fs.promises.lstat = async function lstat(path, options) {
980+
if (typeof path === 'string') {
981+
const vfsResult = findVFSForFsStat(path);
982+
if (vfsResult !== null) {
983+
return vfsResult.stats;
984+
}
985+
}
986+
return origPLstat.call(fs.promises, path, options);
987+
};
988+
989+
const origPReaddir = fs.promises.readdir;
990+
fs.promises.readdir = async function readdir(path, options) {
991+
if (typeof path === 'string') {
992+
const vfsResult = findVFSForReaddir(path, options);
993+
if (vfsResult !== null) {
994+
return vfsResult.entries;
995+
}
996+
}
997+
return origPReaddir.call(fs.promises, path, options);
998+
};
999+
1000+
const origPReadlink = fs.promises.readlink;
1001+
fs.promises.readlink = async function readlink(path, options) {
1002+
if (typeof path === 'string') {
1003+
const vfsResult = findVFSForRealpath(path);
1004+
if (vfsResult !== null) {
1005+
return vfsResult.realpath;
1006+
}
1007+
}
1008+
return origPReadlink.call(fs.promises, path, options);
1009+
};
1010+
1011+
const origPRealpath = fs.promises.realpath;
1012+
fs.promises.realpath = async function realpath(path, options) {
1013+
if (typeof path === 'string') {
1014+
const vfsResult = findVFSForRealpath(path);
1015+
if (vfsResult !== null) {
1016+
return vfsResult.realpath;
1017+
}
1018+
}
1019+
return origPRealpath.call(fs.promises, path, options);
1020+
};
1021+
9041022
originalWatch = fs.watch;
9051023
fs.watch = function watch(filename, options, listener) {
9061024
if (typeof options === 'function') {

0 commit comments

Comments
 (0)