Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error messages #352

Merged
merged 3 commits into from
Aug 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');