diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md
index 46d2467c7234b8..2307333cf1369a 100644
--- a/doc/api/deprecations.md
+++ b/doc/api/deprecations.md
@@ -2518,6 +2518,22 @@ Type: Documentation-only
Prefer [`response.socket`][] over [`response.connection`] and
[`request.socket`][] over [`request.connection`].
+
+### DEP0XXX: `WriteStream.open()` and `ReadStream.open()` are internal
+
+
+Type: Runtime
+
+[`WriteStream.open()`][] and [`ReadStream.open()`][] are undocumented internal
+APIs that do not make sense to use in userland. File streams should always be
+opened through their corresponding factory methods [`fs.createWriteStream()`][]
+and [`fs.createReadStream()`][]) or by passing a file descriptor in options.
+
[`--pending-deprecation`]: cli.html#cli_pending_deprecation
[`--throw-deprecation`]: cli.html#cli_throw_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
@@ -2528,10 +2544,12 @@ Prefer [`response.socket`][] over [`response.connection`] and
[`Decipher`]: crypto.html#crypto_class_decipher
[`EventEmitter.listenerCount(emitter, eventName)`]: events.html#events_eventemitter_listenercount_emitter_eventname
[`REPLServer.clearBufferedCommand()`]: repl.html#repl_replserver_clearbufferedcommand
+[`ReadStream.open()`]: fs.html#fs_class_fs_readstream
[`Server.connections`]: net.html#net_server_connections
[`Server.getConnections()`]: net.html#net_server_getconnections_callback
[`Server.listen({fd: })`]: net.html#net_server_listen_handle_backlog_callback
[`SlowBuffer`]: buffer.html#buffer_class_slowbuffer
+[`WriteStream.open()`]: fs.html#fs_class_fs_writestream
[`assert`]: assert.html
[`asyncResource.runInAsyncScope()`]: async_hooks.html#async_hooks_asyncresource_runinasyncscope_fn_thisarg_args
[`child_process`]: child_process.html
@@ -2554,6 +2572,8 @@ Prefer [`response.socket`][] over [`response.connection`] and
[`ecdh.setPublicKey()`]: crypto.html#crypto_ecdh_setpublickey_publickey_encoding
[`emitter.listenerCount(eventName)`]: events.html#events_emitter_listenercount_eventname
[`fs.access()`]: fs.html#fs_fs_access_path_mode_callback
+[`fs.createReadStream()`]: fs.html#fs_fs_createreadstream_path_options
+[`fs.createWriteStream()`]: fs.html#fs_fs_createwritestream_path_options
[`fs.exists(path, callback)`]: fs.html#fs_fs_exists_path_callback
[`fs.lchmod(path, mode, callback)`]: fs.html#fs_fs_lchmod_path_mode_callback
[`fs.lchmodSync(path, mode)`]: fs.html#fs_fs_lchmodsync_path_mode
diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js
index 110ad114930fca..3674a71f92ba15 100644
--- a/lib/internal/fs/streams.js
+++ b/lib/internal/fs/streams.js
@@ -5,6 +5,7 @@ const { Math, Object } = primordials;
const {
ERR_OUT_OF_RANGE
} = require('internal/errors').codes;
+const internalUtil = require('internal/util');
const { validateNumber } = require('internal/validators');
const fs = require('fs');
const { Buffer } = require('buffer');
@@ -100,7 +101,7 @@ function ReadStream(path, options) {
}
if (typeof this.fd !== 'number')
- this.open();
+ _openReadFs(this);
this.on('end', function() {
if (this.autoClose) {
@@ -111,23 +112,34 @@ function ReadStream(path, options) {
Object.setPrototypeOf(ReadStream.prototype, Readable.prototype);
Object.setPrototypeOf(ReadStream, Readable);
-ReadStream.prototype.open = function() {
- fs.open(this.path, this.flags, this.mode, (er, fd) => {
+const openReadFs = internalUtil.deprecate(function() {
+ _openReadFs(this);
+}, 'ReadStream.prototype.open() is deprecated', 'DEP0XXX');
+ReadStream.prototype.open = openReadFs;
+
+function _openReadFs(stream) {
+ // Backwards compat for overriden open.
+ if (stream.open !== openReadFs) {
+ stream.open();
+ return;
+ }
+
+ fs.open(stream.path, stream.flags, stream.mode, (er, fd) => {
if (er) {
- if (this.autoClose) {
- this.destroy();
+ if (stream.autoClose) {
+ stream.destroy();
}
- this.emit('error', er);
+ stream.emit('error', er);
return;
}
- this.fd = fd;
- this.emit('open', fd);
- this.emit('ready');
+ stream.fd = fd;
+ stream.emit('open', fd);
+ stream.emit('ready');
// Start the flow of data.
- this.read();
+ stream.read();
});
-};
+}
ReadStream.prototype._read = function(n) {
if (typeof this.fd !== 'number') {
@@ -266,7 +278,7 @@ function WriteStream(path, options) {
this.setDefaultEncoding(options.encoding);
if (typeof this.fd !== 'number')
- this.open();
+ _openWriteFs(this);
}
Object.setPrototypeOf(WriteStream.prototype, Writable.prototype);
Object.setPrototypeOf(WriteStream, Writable);
@@ -279,21 +291,32 @@ WriteStream.prototype._final = function(callback) {
callback();
};
-WriteStream.prototype.open = function() {
- fs.open(this.path, this.flags, this.mode, (er, fd) => {
+const openWriteFs = internalUtil.deprecate(function() {
+ _openWriteFs(this);
+}, 'WriteStream.prototype.open() is deprecated', 'DEP0XXX');
+WriteStream.prototype.open = openWriteFs;
+
+function _openWriteFs(stream) {
+ // Backwards compat for overriden open.
+ if (stream.open !== openWriteFs) {
+ stream.open();
+ return;
+ }
+
+ fs.open(stream.path, stream.flags, stream.mode, (er, fd) => {
if (er) {
- if (this.autoClose) {
- this.destroy();
+ if (stream.autoClose) {
+ stream.destroy();
}
- this.emit('error', er);
+ stream.emit('error', er);
return;
}
- this.fd = fd;
- this.emit('open', fd);
- this.emit('ready');
+ stream.fd = fd;
+ stream.emit('open', fd);
+ stream.emit('ready');
});
-};
+}
WriteStream.prototype._write = function(data, encoding, cb) {
diff --git a/test/parallel/test-fs-read-stream-patch-open.js b/test/parallel/test-fs-read-stream-patch-open.js
new file mode 100644
index 00000000000000..80f8888de30042
--- /dev/null
+++ b/test/parallel/test-fs-read-stream-patch-open.js
@@ -0,0 +1,15 @@
+'use strict';
+const common = require('../common');
+const fs = require('fs');
+
+common.expectWarning(
+ 'DeprecationWarning',
+ 'ReadStream.prototype.open() is deprecated', 'DEP0XXX');
+const s = fs.createReadStream('asd')
+ // We don't care about errors in this test.
+ .on('error', () => {});
+s.open();
+
+// Allow overriding open().
+fs.ReadStream.prototype.open = common.mustCall();
+fs.createReadStream('asd');
diff --git a/test/parallel/test-fs-write-stream-patch-open.js b/test/parallel/test-fs-write-stream-patch-open.js
new file mode 100644
index 00000000000000..1c82e80213f2fa
--- /dev/null
+++ b/test/parallel/test-fs-write-stream-patch-open.js
@@ -0,0 +1,34 @@
+'use strict';
+const common = require('../common');
+const fs = require('fs');
+
+const tmpdir = require('../common/tmpdir');
+
+// Run in a child process because 'out' is opened twice, blocking the tmpdir
+// and preventing cleanup.
+if (process.argv[2] !== 'child') {
+ // Parent
+ const assert = require('assert');
+ const { fork } = require('child_process');
+ tmpdir.refresh();
+
+ // Run test
+ const child = fork(__filename, ['child'], { stdio: 'inherit' });
+ child.on('exit', common.mustCall(function(code) {
+ assert.strictEqual(code, 0);
+ }));
+
+ return;
+}
+
+// Child
+
+common.expectWarning(
+ 'DeprecationWarning',
+ 'WriteStream.prototype.open() is deprecated', 'DEP0XXX');
+const s = fs.createWriteStream(`${tmpdir.path}/out`);
+s.open();
+
+// Allow overriding open().
+fs.WriteStream.prototype.open = common.mustCall();
+fs.createWriteStream('asd');