Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 283 additions & 0 deletions lib/module_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,289 @@ function installFsPatches() {
return originalExistsSync.call(fs, path);
};

// Patch fs.readlinkSync for VFS
const originalReadlinkSync = fs.readlinkSync;
fs.readlinkSync = function readlinkSync(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForRealpath(path);
if (vfsResult !== null) {
return vfsResult.realpath;
}
}
return originalReadlinkSync.call(fs, path, options);
};

// --- Async callback patches (fs.access, fs.accessSync) ---

const originalAccessSync = fs.accessSync;
fs.accessSync = function accessSync(path, mode) {
if (typeof path === 'string') {
const vfsResult = findVFSForExists(path);
if (vfsResult !== null) {
if (!vfsResult.exists) {
throw createENOENT('access', path);
}
return;
}
}
return originalAccessSync.call(fs, path, mode);
};

const originalAccess = fs.access;
fs.access = function access(path, mode, callback) {
if (typeof mode === 'function') {
callback = mode;
mode = fs.constants.F_OK;
}
if (typeof path === 'string') {
const vfsResult = findVFSForExists(path);
if (vfsResult !== null) {
const err = vfsResult.exists ? null : createENOENT('access', path);
if (callback) process.nextTick(callback, err);
return;
}
}
return originalAccess.call(fs, path, mode, callback);
};

// --- Callback patches for fs.stat, fs.lstat, fs.readFile, fs.createReadStream ---

const originalStat = fs.stat;
fs.stat = function stat(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForFsStat(path);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.stats);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalStat.call(fs, path, options, callback);
};

const originalLstat = fs.lstat;
fs.lstat = function lstat(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForFsStat(path);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.stats);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalLstat.call(fs, path, options, callback);
};

const originalReadFile = fs.readFile;
fs.readFile = function readFile(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForRead(path, options);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.content);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalReadFile.call(fs, path, options, callback);
};

const originalCreateReadStream = fs.createReadStream;
fs.createReadStream = function createReadStream(path, options) {
if (typeof path === 'string') {
try {
const vfsResult = findVFSForRead(path, options);
if (vfsResult !== null) {
const { Readable } = require('node:stream');
const stream = new Readable({ read() {} });
stream.push(vfsResult.content);
stream.push(null);
return stream;
}
} catch (err) {
const { Readable } = require('node:stream');
const stream = new Readable({ read() {} });
process.nextTick(() => stream.destroy(err));
return stream;
}
}
return originalCreateReadStream.call(fs, path, options);
};

const originalReaddir = fs.readdir;
fs.readdir = function readdir(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForReaddir(path, options);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.entries);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalReaddir.call(fs, path, options, callback);
};

const originalReadlink = fs.readlink;
fs.readlink = function readlink(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForRealpath(path);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.realpath);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalReadlink.call(fs, path, options, callback);
};

const originalRealpath = fs.realpath;
fs.realpath = function realpath(path, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
if (typeof path === 'string') {
try {
const vfsResult = findVFSForRealpath(path);
if (vfsResult !== null) {
if (callback) process.nextTick(callback, null, vfsResult.realpath);
return;
}
} catch (err) {
if (callback) process.nextTick(callback, err);
return;
}
}
return originalRealpath.call(fs, path, options, callback);
};
if (originalRealpath.native) {
fs.realpath.native = originalRealpath.native;
}

// --- fs.promises patches ---
// Patched directly on the shared object so require('fs/promises') also
// picks up the changes (it returns the same reference as fs.promises).
// Fixes: https://github.com/platformatic/vfs/issues/8

const origPAccess = fs.promises.access;
fs.promises.access = async function access(path, mode) {
if (typeof path === 'string') {
const vfsResult = findVFSForExists(path);
if (vfsResult !== null) {
if (!vfsResult.exists) {
throw createENOENT('access', path);
}
return;
}
}
return origPAccess.call(fs.promises, path, mode);
};

const origPReadFile = fs.promises.readFile;
fs.promises.readFile = async function readFile(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForRead(path, options);
if (vfsResult !== null) {
return vfsResult.content;
}
}
return origPReadFile.call(fs.promises, path, options);
};

const origPStat = fs.promises.stat;
fs.promises.stat = async function stat(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForFsStat(path);
if (vfsResult !== null) {
return vfsResult.stats;
}
}
return origPStat.call(fs.promises, path, options);
};

const origPLstat = fs.promises.lstat;
fs.promises.lstat = async function lstat(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForFsStat(path);
if (vfsResult !== null) {
return vfsResult.stats;
}
}
return origPLstat.call(fs.promises, path, options);
};

const origPReaddir = fs.promises.readdir;
fs.promises.readdir = async function readdir(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForReaddir(path, options);
if (vfsResult !== null) {
return vfsResult.entries;
}
}
return origPReaddir.call(fs.promises, path, options);
};

const origPReadlink = fs.promises.readlink;
fs.promises.readlink = async function readlink(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForRealpath(path);
if (vfsResult !== null) {
return vfsResult.realpath;
}
}
return origPReadlink.call(fs.promises, path, options);
};

const origPRealpath = fs.promises.realpath;
fs.promises.realpath = async function realpath(path, options) {
if (typeof path === 'string') {
const vfsResult = findVFSForRealpath(path);
if (vfsResult !== null) {
return vfsResult.realpath;
}
}
return origPRealpath.call(fs.promises, path, options);
};

originalWatch = fs.watch;
fs.watch = function watch(filename, options, listener) {
if (typeof options === 'function') {
Expand Down
Loading
Loading