Skip to content

Commit

Permalink
Fix error messages (#352)
Browse files Browse the repository at this point in the history
* fix error messages to match node 10.8.0

* npm run update-browser-errors

* test errors
  • Loading branch information
vweevers authored and mcollina committed Aug 12, 2018
1 parent 9004c81 commit 3dc117e
Show file tree
Hide file tree
Showing 3 changed files with 299 additions and 14 deletions.
84 changes: 77 additions & 7 deletions errors-browser.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
Expand All @@ -13,21 +15,21 @@ function createErrorType(code, message, Base) {
Base = Error;
}

function getMessage(arg1, arg2) {
function getMessage(arg1, arg2, arg3) {
if (typeof message === 'string') {
return message;
} else {
return message(arg1, arg2);
return message(arg1, arg2, arg3);
}
}

var NodeError = function (_Base) {
_inherits(NodeError, _Base);

function NodeError(arg1, arg2) {
function NodeError(arg1, arg2, arg3) {
_classCallCheck(this, NodeError);

return _possibleConstructorReturn(this, (NodeError.__proto__ || Object.getPrototypeOf(NodeError)).call(this, getMessage(arg1, arg2)));
return _possibleConstructorReturn(this, (NodeError.__proto__ || Object.getPrototypeOf(NodeError)).call(this, getMessage(arg1, arg2, arg3)));
}

return NodeError;
Expand All @@ -39,16 +41,84 @@ function createErrorType(code, message, Base) {
codes[code] = NodeError;
}

// https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js
function oneOf(expected, thing) {
if (Array.isArray(expected)) {
var len = expected.length;
expected = expected.map(function (i) {
return String(i);
});
if (len > 2) {
return 'one of ' + thing + ' ' + expected.slice(0, len - 1).join(', ') + ', or ' + expected[len - 1];
} else if (len === 2) {
return 'one of ' + thing + ' ' + expected[0] + ' or ' + expected[1];
} else {
return 'of ' + thing + ' ' + expected[0];
}
} else {
return 'of ' + thing + ' ' + String(expected);
}
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
function startsWith(str, search, pos) {
return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
function endsWith(str, search, this_len) {
if (this_len === undefined || this_len > str.length) {
this_len = str.length;
}
return str.substring(this_len - search.length, this_len) === search;
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
function includes(str, search, start) {
if (typeof start !== 'number') {
start = 0;
}

if (start + search.length > str.length) {
return false;
} else {
return str.indexOf(search, start) !== -1;
}
}

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', TypeError);
createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) {
// determiner: 'must be' or 'must not be'
var determiner = void 0;
if (typeof expected === 'string' && startsWith(expected, 'not ')) {
determiner = 'must not be';
expected = expected.replace(/^not /, '');
} else {
determiner = 'must be';
}

var msg = void 0;
if (endsWith(name, ' argument')) {
// For cases like 'first argument'
msg = 'The ' + name + ' ' + determiner + ' ' + oneOf(expected, 'type');
} else {
var type = includes(name, '.') ? 'property' : 'argument';
msg = 'The "' + name + '" ' + type + ' ' + determiner + ' ' + oneOf(expected, 'type');
}

msg += '. Received type ' + (typeof actual === 'undefined' ? 'undefined' : _typeof(actual));
return msg;
}, TypeError);
createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF');
createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) {
return 'The ' + name + ' method is not implemented';
});
createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'premature close');
createErrorType('ERR_STREAM_DESTROYED', 'the stream was destroyed');
createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close');
createErrorType('ERR_STREAM_DESTROYED', function (name) {
return 'Cannot call ' + name + ' after a 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');
Expand Down
81 changes: 74 additions & 7 deletions errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ function createErrorType(code, message, Base) {
Base = Error
}

function getMessage (arg1, arg2) {
function getMessage (arg1, arg2, arg3) {
if (typeof message === 'string') {
return message
} else {
return message(arg1, arg2)
return message(arg1, arg2, arg3)
}
}

class NodeError extends Base {
constructor (arg1, arg2) {
super(getMessage(arg1, arg2));
constructor (arg1, arg2, arg3) {
super(getMessage(arg1, arg2, arg3));
}
}

Expand All @@ -27,16 +27,83 @@ function createErrorType(code, message, Base) {
codes[code] = NodeError;
}

// https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js
function oneOf(expected, thing) {
if (Array.isArray(expected)) {
const len = expected.length;
expected = expected.map((i) => String(i));
if (len > 2) {
return `one of ${thing} ${expected.slice(0, len - 1).join(', ')}, or ` +
expected[len - 1];
} else if (len === 2) {
return `one of ${thing} ${expected[0]} or ${expected[1]}`;
} else {
return `of ${thing} ${expected[0]}`;
}
} else {
return `of ${thing} ${String(expected)}`;
}
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
function startsWith(str, search, pos) {
return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
function endsWith(str, search, this_len) {
if (this_len === undefined || this_len > str.length) {
this_len = str.length;
}
return str.substring(this_len - search.length, this_len) === search;
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
function includes(str, search, start) {
if (typeof start !== 'number') {
start = 0;
}

if (start + search.length > str.length) {
return false;
} else {
return str.indexOf(search, start) !== -1;
}
}

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', TypeError);
createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) {
// determiner: 'must be' or 'must not be'
let determiner;
if (typeof expected === 'string' && startsWith(expected, 'not ')) {
determiner = 'must not be';
expected = expected.replace(/^not /, '');
} else {
determiner = 'must be';
}

let msg;
if (endsWith(name, ' argument')) {
// For cases like 'first argument'
msg = `The ${name} ${determiner} ${oneOf(expected, 'type')}`;
} else {
const type = includes(name, '.') ? 'property' : 'argument';
msg = `The "${name}" ${type} ${determiner} ${oneOf(expected, 'type')}`;
}

msg += `. Received type ${typeof actual}`;
return msg;
}, TypeError);
createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF');
createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) {
return 'The ' + name + ' method is not implemented'
});
createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'premature close');
createErrorType('ERR_STREAM_DESTROYED', 'the stream was destroyed');
createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close');
createErrorType('ERR_STREAM_DESTROYED', function (name) {
return 'Cannot call ' + name + ' after a 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');
Expand Down
148 changes: 148 additions & 0 deletions test/ours/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
var tap = require('tap');
var assert = require('assert');
var errors = require('../../errors').codes;

function expect (err, Base, name, code, message) {
assert(err instanceof Base);
assert.strictEqual(err.name, name);
assert.strictEqual(err.code, code);
assert.strictEqual(err.message, message);
}

expect(
new errors.ERR_INVALID_OPT_VALUE('name', 0),
TypeError,
'TypeError',
'ERR_INVALID_OPT_VALUE',
'The value "0" is invalid for option "name"'
);

expect(
new errors.ERR_INVALID_OPT_VALUE('name', undefined),
TypeError,
'TypeError',
'ERR_INVALID_OPT_VALUE',
'The value "undefined" is invalid for option "name"'
);

expect(
new errors.ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], 0),
TypeError,
'TypeError',
'ERR_INVALID_ARG_TYPE',
'The "chunk" argument must be one of type string, Buffer, or Uint8Array. Received type number'
);

expect(
new errors.ERR_INVALID_ARG_TYPE('first argument', 'not string', 'foo'),
TypeError,
'TypeError',
'ERR_INVALID_ARG_TYPE',
'The first argument must not be of type string. Received type string'
);

expect(
new errors.ERR_INVALID_ARG_TYPE('obj.prop', 'string', undefined),
TypeError,
'TypeError',
'ERR_INVALID_ARG_TYPE',
'The "obj.prop" property must be of type string. Received type undefined'
);

expect(
new errors.ERR_STREAM_PUSH_AFTER_EOF(),
Error,
'Error',
'ERR_STREAM_PUSH_AFTER_EOF',
'stream.push() after EOF'
);

expect(
new errors.ERR_METHOD_NOT_IMPLEMENTED('_read()'),
Error,
'Error',
'ERR_METHOD_NOT_IMPLEMENTED',
'The _read() method is not implemented'
);

expect(
new errors.ERR_METHOD_NOT_IMPLEMENTED('_write()'),
Error,
'Error',
'ERR_METHOD_NOT_IMPLEMENTED',
'The _write() method is not implemented'
);

expect(
new errors.ERR_STREAM_PREMATURE_CLOSE(),
Error,
'Error',
'ERR_STREAM_PREMATURE_CLOSE',
'Premature close'
);

expect(
new errors.ERR_STREAM_DESTROYED('pipe'),
Error,
'Error',
'ERR_STREAM_DESTROYED',
'Cannot call pipe after a stream was destroyed'
);

expect(
new errors.ERR_STREAM_DESTROYED('write'),
Error,
'Error',
'ERR_STREAM_DESTROYED',
'Cannot call write after a stream was destroyed'
);

expect(
new errors.ERR_MULTIPLE_CALLBACK(),
Error,
'Error',
'ERR_MULTIPLE_CALLBACK',
'Callback called multiple times'
);

expect(
new errors.ERR_STREAM_CANNOT_PIPE(),
Error,
'Error',
'ERR_STREAM_CANNOT_PIPE',
'Cannot pipe, not readable'
);

expect(
new errors.ERR_STREAM_WRITE_AFTER_END(),
Error,
'Error',
'ERR_STREAM_WRITE_AFTER_END',
'write after end'
);

expect(
new errors.ERR_STREAM_NULL_VALUES(),
TypeError,
'TypeError',
'ERR_STREAM_NULL_VALUES',
'May not write null values to stream'
);

expect(
new errors.ERR_UNKNOWN_ENCODING('foo'),
TypeError,
'TypeError',
'ERR_UNKNOWN_ENCODING',
'Unknown encoding: foo'
);

expect(
new errors.ERR_STREAM_UNSHIFT_AFTER_END_EVENT(),
Error,
'Error',
'ERR_STREAM_UNSHIFT_AFTER_END_EVENT',
'stream.unshift() after end event'
);

require('tap').pass('sync done');

0 comments on commit 3dc117e

Please sign in to comment.