diff --git a/README.md b/README.md index d0fafcf20f..bba15c79d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # readable-stream -***Node-core v10.1.0 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream) +***Node-core v10.3.0 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream) [![NPM](https://nodei.co/npm/readable-stream.png?downloads=true&downloadRank=true)](https://nodei.co/npm/readable-stream/) @@ -18,7 +18,7 @@ npm install --save readable-stream This package is a mirror of the Streams2 and Streams3 implementations in Node-core. -Full documentation may be found on the [Node.js website](https://nodejs.org/dist/v10.1.0/docs/api/stream.html). +Full documentation may be found on the [Node.js website](https://nodejs.org/dist/v10.3.0/docs/api/stream.html). If you want to guarantee a stable streams base, regardless of what version of Node you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *"stream"* module in Node-core, for background see [this blogpost](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html). diff --git a/build/test-replacements.js b/build/test-replacements.js index 12843a8428..1241aa80d4 100644 --- a/build/test-replacements.js +++ b/build/test-replacements.js @@ -12,6 +12,11 @@ const altForEachImplReplacement = require('./common-replacements').altForEachImp require('./common-replacements').bufferStaticMethods , specialForEachReplacment = require('./common-replacements').specialForEachReplacment + , deepStrictEqual = [ + /util\.isDeepStrictEqual/, + 'require(\'deep-strict-equal\')' + ] + module.exports.all = [ [ @@ -78,6 +83,7 @@ module.exports['common.js'] = [ , objectKeysReplacement , altForEachImplReplacement , altForEachUseReplacement + , deepStrictEqual , [ /(exports.mustCall[\s\S]*)/m @@ -110,7 +116,7 @@ module.exports['common.js'] = [ + ' knownGlobals.push(DTRACE_NET_SOCKET_WRITE);\n' + ' if (global.__coverage__)\n' + ' knownGlobals.push(__coverage__);\n' - + '\'core,__core-js_shared__,Promise,Map,Set,WeakMap,WeakSet,Reflect,System,asap,Observable,regeneratorRuntime,_babelPolyfill\'.split(\',\').filter(function (item) { return typeof global[item] !== undefined}).forEach(function (item) {knownGlobals.push(global[item])})' + + '\'core,__core-js_shared__,console,Promise,Map,Set,WeakMap,WeakSet,Reflect,System,asap,Observable,regeneratorRuntime,_babelPolyfill\'.split(\',\').filter(function (item) { return typeof global[item] !== undefined}).forEach(function (item) {knownGlobals.push(global[item])})' + ' /**/\n\n$1' ] @@ -338,6 +344,22 @@ module.exports['test-stream-unpipe-event.js'] = [ ] ] +module.exports['test-stream-readable-flow-recursion.js'] = [ + deepStrictEqual +] + +module.exports['test-stream-readable-with-unimplemented-_read.js'] = [ + deepStrictEqual +] + +module.exports['test-stream-writable-needdrain-state.js'] = [ + deepStrictEqual +] + +module.exports['test-stream-readable-setEncoding-null.js'] = [ + deepStrictEqual +] + module.exports['test-stream-pipeline.js'] = [ [ /require\('http2'\)/g, diff --git a/errors.js b/errors.js index 5b8c455772..b17eeea660 100644 --- a/errors.js +++ b/errors.js @@ -2,26 +2,43 @@ const codes = {}; -function createErrorType(name) { - function E(message) { - if (!Error.captureStackTrace) - this.stack = (new Error()).stack; - else - Error.captureStackTrace(this, this.constructor); - this.message = message; +function createErrorType(code, message, Base) { + if (!Base) { + Base = Error } - E.prototype = new Error(); - E.prototype.name = name; - E.prototype.constructor = E; - codes[name] = E; + function getMessage (arg1, arg2) { + if (typeof message === 'string') { + return message + } else { + return message(arg1, arg2) + } + } + + // TODO(mcollina) make this a function + class NodeError extends Base { + constructor (arg1, arg2) { + super(getMessage(arg1, arg2)); + } + } + + NodeError.prototype.name = Base.name; + NodeError.prototype.code = code; + + codes[code] = NodeError; } -createErrorType('ERR_INVALID_OPT_VALUE'); -createErrorType('ERR_INVALID_ARG_TYPE'); -createErrorType('ERR_STREAM_PUSH_AFTER_EOF'); -createErrorType('ERR_METHOD_NOT_IMPLEMENTED'); -createErrorType('ERR_STREAM_PUSH_AFTER_EOF'); -createErrorType('ERR_STREAM_PREMATURE_CLOSE'); +createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) { + return 'The value "' + value + '" is invalid for option "' + name + '"' +}, TypeError); +createErrorType('ERR_INVALID_ARG_TYPE', 'argument must be of the right type'); +createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream-push() after EOF'); +createErrorType('ERR_METHOD_NOT_IMPLEMENTED', 'the method is not implemented'); +createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'premature close'); +createErrorType('ERR_STREAM_DESTROYED', 'the stream was destroyed'); +createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times'); +createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable'); +createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end'); +createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); module.exports.codes = codes; diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 6f295c95b2..c9823bb68b 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -86,12 +86,14 @@ var _require$codes = require('../errors').codes, ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED, ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; -var ReadableAsyncIterator = require('./internal/streams/async_iterator'); - var _require2 = require('../experimentalWarning'), emitExperimentalWarning = _require2.emitExperimentalWarning; -var StringDecoder; +// Lazy loaded to improve the startup performance. + + +var StringDecoder = void 0; +var ReadableAsyncIterator = void 0; util.inherits(Readable, Stream); @@ -345,7 +347,8 @@ Readable.prototype.isPaused = function () { Readable.prototype.setEncoding = function (enc) { if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder; this._readableState.decoder = new StringDecoder(enc); - this._readableState.encoding = enc; + // if setEncoding(null), decoder.encoding equals utf8 + this._readableState.encoding = this._readableState.decoder.encoding; return this; }; @@ -804,7 +807,7 @@ Readable.prototype.removeListener = function (ev, fn) { if (ev === 'readable') { // We need to check if there is someone still listening to - // to readable and reset the state. However this needs to happen + // readable and reset the state. However this needs to happen // after readable has been emitted but before I/O (nextTick) to // support once('readable', fn) cycles. This means that calling // resume within the same tick will have no @@ -816,11 +819,11 @@ Readable.prototype.removeListener = function (ev, fn) { }; Readable.prototype.removeAllListeners = function (ev) { - var res = Stream.prototype.removeAllListeners.call(this, ev); + var res = Stream.prototype.removeAllListeners.apply(this, arguments); if (ev === 'readable' || ev === undefined) { // We need to check if there is someone still listening to - // to readable and reset the state. However this needs to happen + // readable and reset the state. However this needs to happen // after readable has been emitted but before I/O (nextTick) to // support once('readable', fn) cycles. This means that calling // resume within the same tick will have no @@ -954,7 +957,7 @@ Readable.prototype.wrap = function (stream) { Readable.prototype[Symbol.asyncIterator] = function () { emitExperimentalWarning('Readable[Symbol.asyncIterator]'); - + if (ReadableAsyncIterator === undefined) ReadableAsyncIterator = require('./internal/streams/async_iterator'); return new ReadableAsyncIterator(this); }; diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index c63861de4c..ec6c075154 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -307,8 +307,7 @@ function writeAfterEnd(stream, cb) { // mode the stream is in. Currently this means that `null` is never accepted // and undefined/non-string values are only allowed in object mode. function validChunk(stream, state, chunk, cb) { - var valid = true; - var er = false; + var er; if (chunk === null) { er = new ERR_STREAM_NULL_VALUES(); @@ -318,9 +317,9 @@ function validChunk(stream, state, chunk, cb) { if (er) { stream.emit('error', er); pna.nextTick(cb, er); - valid = false; + return false; } - return valid; + return true; } Writable.prototype.write = function (chunk, encoding, cb) { @@ -350,9 +349,7 @@ Writable.prototype.write = function (chunk, encoding, cb) { }; Writable.prototype.cork = function () { - var state = this._writableState; - - state.corked++; + this._writableState.corked++; }; Writable.prototype.uncork = function () { diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 56c67af8e5..8ec2395556 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -8,7 +8,7 @@ var pna = require('process-nextick-args'); /**/ -var eos = require('./end-of-stream'); +var eos = void 0; var _require$codes = require('../../../errors').codes, ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS, @@ -37,6 +37,7 @@ function destroyer(stream, reading, writing, callback) { closed = true; }); + if (eos === undefined) eos = require('./end-of-stream'); eos(stream, { readable: reading, writable: writing }, function (err) { if (err) return callback(err); closed = true; diff --git a/lib/internal/streams/state.js b/lib/internal/streams/state.js index 7786caf326..1632e883f8 100644 --- a/lib/internal/streams/state.js +++ b/lib/internal/streams/state.js @@ -7,17 +7,18 @@ var pna = require('process-nextick-args'); var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE; +function highWaterMarkFrom(options, isDuplex, duplexKey) { + return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null; +} + function getHighWaterMark(state, options, duplexKey, isDuplex) { - var hwm = options.highWaterMark; + var hwm = highWaterMarkFrom(options, isDuplex, duplexKey); if (hwm != null) { - if (typeof hwm !== 'number' || !(hwm >= 0)) throw new ERR_INVALID_OPT_VALUE('highWaterMark', hwm); - return Math.floor(hwm); - } else if (isDuplex) { - hwm = options[duplexKey]; - if (hwm != null) { - if (typeof hwm !== 'number' || !(hwm >= 0)) throw new ERR_INVALID_OPT_VALUE(duplexKey, hwm); - return Math.floor(hwm); + if (!Number.isInteger(hwm) || hwm < 0) { + var name = isDuplex ? duplexKey : 'highWaterMark'; + throw new ERR_INVALID_OPT_VALUE(name, hwm); } + return Math.floor(hwm); } // Default value diff --git a/package.json b/package.json index 88fefba4fc..c4dc57832f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "assert": "^1.4.0", "babel-polyfill": "^6.9.1", "buffer": "^5.1.0", + "deep-strict-equal": "^0.2.0", "lolex": "^2.6.0", "nyc": "^11.0.0", "tap": "^11.0.0", diff --git a/test/common/README.md b/test/common/README.md index 41f61baa75..46769f66dd 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -166,11 +166,6 @@ Attempts to get a valid TTY file descriptor. Returns `-1` if it fails. The TTY file descriptor is assumed to be capable of being writable. -### globalCheck -* [<boolean>] - -Set to `false` if the test should not check for global leaks. - ### hasCrypto * [<boolean>] diff --git a/test/common/index.js b/test/common/index.js index a3916882ac..4ffb2714fe 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -348,8 +348,7 @@ exports.platformTimeout = function (ms) { return ms; // ARMv8+ }; -var knownGlobals = [Buffer, clearImmediate, clearInterval, clearTimeout, console, constructor, // Enumerable in V8 3.21. -global, process, setImmediate, setInterval, setTimeout]; +var knownGlobals = [Buffer, clearImmediate, clearInterval, clearTimeout, global, process, setImmediate, setInterval, setTimeout]; if (global.gc) { knownGlobals.push(global.gc); @@ -373,31 +372,6 @@ if (global.COUNTER_NET_SERVER_CONNECTION) { knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE); } -/**/if (!process.browser) { - if (global.ArrayBuffer) { - knownGlobals.push(ArrayBuffer); - knownGlobals.push(Int8Array); - knownGlobals.push(Uint8Array); - knownGlobals.push(Uint8ClampedArray); - knownGlobals.push(Int16Array); - knownGlobals.push(Uint16Array); - knownGlobals.push(Int32Array); - knownGlobals.push(Uint32Array); - knownGlobals.push(Float32Array); - knownGlobals.push(Float64Array); - knownGlobals.push(DataView); - } -} /**/ - -// Harmony features. -if (global.Proxy) { - knownGlobals.push(Proxy); -} - -if (global.Symbol) { - knownGlobals.push(Symbol); -} - if (process.env.NODE_TEST_KNOWN_GLOBALS) { var knownFromEnv = process.env.NODE_TEST_KNOWN_GLOBALS.split(','); allowGlobals.apply(undefined, _toConsumableArray(knownFromEnv)); @@ -417,7 +391,7 @@ if (typeof constructor == 'function') knownGlobals.push(constructor); if (typeof DTRACE_NET_SOCKET_READ == 'function') knownGlobals.push(DTRACE_NET_SOCKET_READ); if (typeof DTRACE_NET_SOCKET_WRITE == 'function') knownGlobals.push(DTRACE_NET_SOCKET_WRITE); if (global.__coverage__) knownGlobals.push(__coverage__); -'core,__core-js_shared__,Promise,Map,Set,WeakMap,WeakSet,Reflect,System,asap,Observable,regeneratorRuntime,_babelPolyfill'.split(',').filter(function (item) { +'core,__core-js_shared__,console,Promise,Map,Set,WeakMap,WeakSet,Reflect,System,asap,Observable,regeneratorRuntime,_babelPolyfill'.split(',').filter(function (item) { return typeof global[item] !== undefined; }).forEach(function (item) { knownGlobals.push(global[item]); @@ -442,11 +416,7 @@ function leakedGlobals() { } exports.leakedGlobals = leakedGlobals; -// Turn this off if the test should not check for global leaks. -exports.globalCheck = true; - process.on('exit', function () { - if (!exports.globalCheck) return; var leaked = leakedGlobals(); if (leaked.length > 0) { assert.fail('Unexpected global(s) found: ' + leaked.join(', ')); @@ -820,7 +790,7 @@ exports.expectsError = function expectsError(fn, settings, exact) { for (var _iterator2 = keys[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var key = _step2.value; - if (!util.isDeepStrictEqual(error[key], innerSettings[key])) { + if (!require('deep-strict-equal')(error[key], innerSettings[key])) { // Create placeholder objects to create a nice output. var a = new Comparison(error, keys); var b = new Comparison(innerSettings, keys); @@ -1015,4 +985,4 @@ if (!util._errnoException) { e.syscall = syscall; return e; }; -} \ No newline at end of file +} diff --git a/test/common/index.mjs b/test/common/index.mjs index 4ae4dcd7f0..32533c6bf3 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -24,8 +24,6 @@ let knownGlobals = [ clearImmediate, clearInterval, clearTimeout, - console, - constructor, // Enumerable in V8 3.21. global, process, setImmediate, @@ -66,29 +64,6 @@ export function leakedGlobals() { knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE); } - if (global.ArrayBuffer) { - knownGlobals.push(ArrayBuffer); - knownGlobals.push(Int8Array); - knownGlobals.push(Uint8Array); - knownGlobals.push(Uint8ClampedArray); - knownGlobals.push(Int16Array); - knownGlobals.push(Uint16Array); - knownGlobals.push(Int32Array); - knownGlobals.push(Uint32Array); - knownGlobals.push(Float32Array); - knownGlobals.push(Float64Array); - knownGlobals.push(DataView); - } - - // Harmony features. - if (global.Proxy) { - knownGlobals.push(Proxy); - } - - if (global.Symbol) { - knownGlobals.push(Symbol); - } - const leaked = []; for (const val in global) { @@ -104,11 +79,7 @@ export function leakedGlobals() { } } -// Turn this off if the test should not check for global leaks. -export let globalCheck = true; // eslint-disable-line - process.on('exit', function() { - if (!globalCheck) return; const leaked = leakedGlobals(); if (leaked.length > 0) { assert.fail(`Unexpected global(s) found: ${leaked.join(', ')}`); diff --git a/test/ours/lolex-fake-timers.js b/test/ours/lolex-fake-timers.js index 8a43c8da78..59af1328ea 100644 --- a/test/ours/lolex-fake-timers.js +++ b/test/ours/lolex-fake-timers.js @@ -38,4 +38,4 @@ stream.emit('data'); clock.runAll() clock.uninstall(); assert(stream2DataCalled); -t.pass('ok'); +tap.pass('ok'); diff --git a/test/parallel/test-stream-pipe-await-drain-push-while-write.js b/test/parallel/test-stream-pipe-await-drain-push-while-write.js index 2f8f57ea2b..714647574b 100644 --- a/test/parallel/test-stream-pipe-await-drain-push-while-write.js +++ b/test/parallel/test-stream-pipe-await-drain-push-while-write.js @@ -7,7 +7,7 @@ var assert = require('assert/'); var writable = new stream.Writable({ write: common.mustCall(function (chunk, encoding, cb) { - assert.strictEqual(readable._readableState.awaitDrain, 0, 'State variable awaitDrain is not correct.'); + assert.strictEqual(readable._readableState.awaitDrain, 0); if (chunk.length === 32 * 1024) { // first chunk @@ -15,7 +15,7 @@ var writable = new stream.Writable({ // We should check if awaitDrain counter is increased in the next // tick, because awaitDrain is incremented after this method finished process.nextTick(function () { - assert.strictEqual(readable._readableState.awaitDrain, 1, 'Counter is not increased for awaitDrain'); + assert.strictEqual(readable._readableState.awaitDrain, 1); }); } diff --git a/test/parallel/test-stream-readable-event.js b/test/parallel/test-stream-readable-event.js index b9ba727aff..2553fd791e 100644 --- a/test/parallel/test-stream-readable-event.js +++ b/test/parallel/test-stream-readable-event.js @@ -119,4 +119,17 @@ var Readable = require('../../').Readable; assert.deepStrictEqual(result, expected); })); } + +{ + // #20923 + var _r4 = new Readable(); + _r4._read = function () { + // actually doing thing here + }; + _r4.on('data', function () {}); + + _r4.removeAllListeners(); + + assert.strictEqual(_r4.eventNames().length, 0); +} ;require('tap').pass('sync run'); \ No newline at end of file diff --git a/test/parallel/test-stream-readable-setEncoding-null.js b/test/parallel/test-stream-readable-setEncoding-null.js new file mode 100644 index 0000000000..b557c63239 --- /dev/null +++ b/test/parallel/test-stream-readable-setEncoding-null.js @@ -0,0 +1,19 @@ +/**/ +var bufferShim = require('safe-buffer').Buffer; +/**/ + +require('../common'); +var assert = require('assert/'); + +var _require = require('../../'), + Readable = _require.Readable; + +{ + var readable = new Readable({ encoding: 'hex' }); + assert.strictEqual(readable._readableState.encoding, 'hex'); + + readable.setEncoding(null); + + assert.strictEqual(readable._readableState.encoding, 'utf8'); +} +;require('tap').pass('sync run'); \ No newline at end of file