From eb24e2fddc57313027a8281f9ced6fc169252cd9 Mon Sep 17 00:00:00 2001 From: Chunpeng Huo Date: Fri, 14 Aug 2020 09:38:58 +1000 Subject: [PATCH] fix: synchronize patching binding/createWriteStream/cwd/chdir All the patches now check same realBinding._mockedBinding. This should fix the edge cases around the new feature bypass(). follows up #306 --- lib/index.js | 92 +++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 58 deletions(-) diff --git a/lib/index.js b/lib/index.js index 2f6b5ac6..efe047db 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,21 +3,39 @@ const Binding = require('./binding'); const FSError = require('./error'); const FileSystem = require('./filesystem'); -const realBinding = process.binding('fs'); const path = require('path'); const loader = require('./loader'); const bypass = require('./bypass'); const fs = require('fs'); +const realBinding = process.binding('fs'); const toNamespacedPath = FileSystem.toNamespacedPath; +const realCreateWriteStream = fs.createWriteStream; +const realCwd = process.cwd; +const realChdir = process.chdir; -const realProcessProps = { - cwd: process.cwd, - chdir: process.chdir +let mockedCwd; // To be set in mockfs() +process.cwd = function() { + if (realBinding._mockedBinding) { + return mockedCwd; + } + return realCwd(); +}; + +process.chdir = function(directory) { + if (realBinding._mockedBinding) { + if ( + !realBinding._mockedBinding + .stat(toNamespacedPath(directory)) + .isDirectory() + ) { + throw new FSError('ENOTDIR'); + } + mockedCwd = path.resolve(mockedCwd, directory); + } else { + return realChdir(); + } }; -const realCreateWriteStream = fs.createWriteStream; -const realStats = realBinding.Stats; -const realStatWatcher = realBinding.StatWatcher; /** * Pre-patch fs binding. @@ -50,15 +68,6 @@ for (const key in Binding.prototype) { } } -function overrideBinding(binding) { - realBinding._mockedBinding = binding; -} - -function overrideProcess(cwd, chdir) { - process.cwd = cwd; - process.chdir = chdir; -} - /** * Have to disable write stream _writev on nodejs v10+. * @@ -83,30 +92,14 @@ function overrideProcess(cwd, chdir) { * Luckily _writev is an optional method on Writeable stream implementation. * When _writev is missing, it will fall back to make multiple _write calls. */ -function overrideCreateWriteStream() { - fs.createWriteStream = function(path, options) { - const output = realCreateWriteStream(path, options); - // disable _writev, this will over shadow WriteStream.prototype._writev +fs.createWriteStream = function(path, options) { + const output = realCreateWriteStream(path, options); + // disable _writev, this will over shadow WriteStream.prototype._writev + if (realBinding._mockedBinding) { output._writev = undefined; - return output; - }; -} - -function restoreBinding() { - delete realBinding._mockedBinding; - realBinding.Stats = realStats; - realBinding.StatWatcher = realStatWatcher; -} - -function restoreProcess() { - for (const key in realProcessProps) { - process[key] = realProcessProps[key]; } -} - -function restoreCreateWriteStream() { - fs.createWriteStream = realCreateWriteStream; -} + return output; +}; /** * Swap out the fs bindings for a mock file system. @@ -120,23 +113,8 @@ function restoreCreateWriteStream() { exports = module.exports = function mock(config, options) { const system = FileSystem.create(config, options); const binding = new Binding(system); - - overrideBinding(binding); - - let currentPath = process.cwd(); - overrideProcess( - function cwd() { - return currentPath; - }, - function chdir(directory) { - if (!binding.stat(toNamespacedPath(directory)).isDirectory()) { - throw new FSError('ENOTDIR'); - } - currentPath = path.resolve(currentPath, directory); - } - ); - - overrideCreateWriteStream(); + mockedCwd = realCwd(); + realBinding._mockedBinding = binding; }; /** @@ -155,9 +133,7 @@ exports.getMockRoot = function() { * Restore the fs bindings for the real file system. */ exports.restore = function() { - restoreBinding(); - restoreProcess(); - restoreCreateWriteStream(); + delete realBinding._mockedBinding; }; /**