text: 'use strict';
  
  const codes = {};
  
  function createErrorType(code, message, Base) {
    if (!Base) {
      Base = Error
    }
  
    function getMessage (arg1, arg2, arg3) {
      if (typeof message === 'string') {
        return message
      } else {
        return message(arg1, arg2, arg3)
      }
    }
  
    class NodeError extends Base {
      constructor (arg1, arg2, arg3) {
        super(getMessage(arg1, arg2, arg3));
      }
    }
  
    NodeError.prototype.name = Base.name;
    NodeError.prototype.code = code;
  
    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_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);
  
  module.exports.codes = codes;