diff --git a/lib/error.js b/lib/error.js index a5751fd..d4c3a1b 100644 --- a/lib/error.js +++ b/lib/error.js @@ -41,7 +41,8 @@ FSError.prototype = new Error(); FSError.codes = codes; /** - * Creates an abort error for when an asynchronous task was aborted. + * Create an abort error for when an asynchronous task was aborted. + * @constructor */ function AbortError() { Error.call(this); @@ -51,7 +52,10 @@ function AbortError() { } /** - * Error constructor. + * FSError constructor. */ exports.FSError = FSError; +/** + * AbortError constructor. + */ exports.AbortError = AbortError; diff --git a/lib/readfilecontext.js b/lib/readfilecontext.js index 6985082..699b6cb 100644 --- a/lib/readfilecontext.js +++ b/lib/readfilecontext.js @@ -3,10 +3,12 @@ const {AbortError} = require('./error'); const {FSReqCallback} = process.binding('fs'); -const kReadFileUnknownBufferLength = 64 * 1024; -const kReadFileBufferLength = 512 * 1024; - -function getReadFileContextPrototype() { +/** + * This is a workaround for getting access to the ReadFileContext + * prototype, which we need to be able to patch its methods. + * @returns {object} + */ +exports.getReadFileContextPrototype = function() { const fs = require('fs'); const fsBinding = process.binding('fs'); @@ -22,12 +24,27 @@ function getReadFileContextPrototype() { fsBinding.open = originalOpen; return proto; -} - -function patchReadFileContext(prototype) { +}; + +/** + * This patches the ReadFileContext prototype to use mocked bindings + * when available. This entire implementation is more or less fully + * copied over from Node.js's /lib/internal/fs/read_file_context.js + * + * This patch is required to support Node.js v16+, where the ReadFileContext + * closes directly over the internal fs bindings, and is also eagerly loader. + * + * See https://github.com/tschaub/mock-fs/issues/332 for more information. + * + * @param {object} prototype The ReadFileContext prototype object to patch. + */ +exports.patchReadFileContext = function(prototype) { const origRead = prototype.read; const origClose = prototype.close; + const kReadFileUnknownBufferLength = 64 * 1024; + const kReadFileBufferLength = 512 * 1024; + function readFileAfterRead(err, bytesRead) { const context = this.context; @@ -57,6 +74,7 @@ function patchReadFileContext(prototype) { let buffer = null; if (context.err || err) { + // This is a simplification from Node.js, where we don't bother merging the errors return callback(context.err || err); } @@ -106,6 +124,9 @@ function patchReadFileContext(prototype) { req.oncomplete = readFileAfterRead; req.context = this; + // This call and the one in close() is what we want to change, the + // rest is pretty much the same as Node.js except we don't have access + // to some of the internal optimizations. prototype._mockedBinding.read(this.fd, buffer, offset, length, -1, req); }; @@ -128,8 +149,4 @@ function patchReadFileContext(prototype) { prototype._mockedBinding.close(this.fd, req); }; -} - -exports.patchReadFileContext = patchReadFileContext; - -exports.getReadFileContextPrototype = getReadFileContextPrototype; +};