@academyjs/rover
Version:
Rover allows you to learn programming interactively.
583 lines • 19.8 kB
JavaScript
;
/**
* Various utility functions used throughout Mocha's codebase.
* @module utils
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMochaID = exports.assignNewMochaID = exports.uniqueID = exports.constants = exports.castArray = exports.isBrowser = exports.cwd = exports.defineConstants = exports.createMap = exports.noop = exports.clamp = exports.isPromise = exports.stackTraceFilter = exports.canonicalize = exports.jsonStringify = exports.stringify = exports.type = exports.canonicalType = exports.clean = exports.slug = exports.isString = exports.escape = exports.inherits = void 0;
const non_secure_1 = require("nanoid/non-secure");
const path_1 = __importDefault(require("path"));
const util_1 = __importDefault(require("util"));
const he_1 = __importDefault(require("he"));
const MOCHA_ID_PROP_NAME = "__mocha_id__";
/**
* Inherit the prototype methods from one constructor into another.
*
* @param {function} ctor - Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor - Constructor function to inherit prototype from.
* @throws {TypeError} if either constructor is null, or if super constructor
* lacks a prototype.
*/
exports.inherits = util_1.default.inherits;
/**
* Escape special characters in the given string of html.
*
* @private
* @param {string} html
* @return {string}
*/
const escape = (html) => he_1.default.encode(String(html), { useNamedReferences: false });
exports.escape = escape;
/**
* Test if the given obj is type of string.
*
* @private
* @param {Object} obj
* @return {boolean}
*/
const isString = (obj) => typeof obj === "string";
exports.isString = isString;
/**
* Compute a slug from the given `str`.
*
* @private
* @param {string} text
* @return {string}
*/
const slug = (text) => text
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^-\w]/g, "")
.replace(/-{2,}/g, "-");
exports.slug = slug;
/**
* Strip the function definition from `str`, and re-indent for pre whitespace.
*
* @param {string} str
* @return {string}
*/
const clean = (str) => {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, "\n")
.replace(/^\uFEFF/, "")
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
.replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, "$1$2$3");
var spaces = str.match(/^\n?( *)/)[1].length;
var tabs = str.match(/^\n?(\t*)/)[1].length;
var re = new RegExp("^\n?" + (tabs ? "\t" : " ") + "{" + (tabs || spaces) + "}", "gm");
str = str.replace(re, "");
return str.trim();
};
exports.clean = clean;
/**
* If a value could have properties, and has none, this function is called,
* which returns a string representation of the empty value.
*
* Functions w/ no properties return `'[Function]'`
* Arrays w/ length === 0 return `'[]'`
* Objects w/ no properties return `'{}'`
* All else: return result of `value.toString()`
*
* @private
* @param {*} value The value to inspect.
* @param {string} typeHint The type of the value
* @returns {string}
*/
const emptyRepresentation = (value, typeHint) => {
switch (typeHint) {
case "function":
return "[Function]";
case "object":
return "{}";
case "array":
return "[]";
default:
return value.toString();
}
};
/**
* Takes some variable and asks `Object.prototype.toString()` what it thinks it
* is.
*
* @private
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
* @param {*} value The value to test.
* @returns {string} Computed type
* @example
* canonicalType({}) // 'object'
* canonicalType([]) // 'array'
* canonicalType(1) // 'number'
* canonicalType(false) // 'boolean'
* canonicalType(Infinity) // 'number'
* canonicalType(null) // 'null'
* canonicalType(new Date()) // 'date'
* canonicalType(/foo/) // 'regexp'
* canonicalType('type') // 'string'
* canonicalType(global) // 'global'
* canonicalType(new String('foo') // 'object'
* canonicalType(async function() {}) // 'asyncfunction'
* canonicalType(await import(name)) // 'module'
*/
const canonicalType = (value) => {
if (value === undefined) {
return "undefined";
}
else if (value === null) {
return "null";
}
else if (Buffer.isBuffer(value)) {
return "buffer";
}
return Object.prototype.toString
.call(value)
.replace(/^\[.+\s(.+?)]$/, "$1")
.toLowerCase();
};
exports.canonicalType = canonicalType;
/**
*
* Returns a general type or data structure of a variable
* @private
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
* @param {*} value The value to test.
* @returns {string} One of undefined, boolean, number, string, bigint, symbol, object
* @example
* type({}) // 'object'
* type([]) // 'array'
* type(1) // 'number'
* type(false) // 'boolean'
* type(Infinity) // 'number'
* type(null) // 'null'
* type(new Date()) // 'object'
* type(/foo/) // 'object'
* type('type') // 'string'
* type(global) // 'object'
* type(new String('foo') // 'string'
*/
const type = (value) => {
// Null is special
if (value === null)
return "null";
const primitives = new Set([
"undefined",
"boolean",
"number",
"string",
"bigint",
"symbol",
]);
const _type = typeof value;
if (_type === "function")
return _type;
if (primitives.has(_type))
return _type;
if (value instanceof String)
return "string";
if (value instanceof Error)
return "error";
if (Array.isArray(value))
return "array";
return _type;
};
exports.type = type;
/**
* Stringify `value`. Different behavior depending on type of value:
*
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
* - If `value` is an *empty* object, function, or array, return result of function
* {@link emptyRepresentation}.
* - If `value` has properties, call {@link canonicalize} on it, then return result of
* JSON.stringify().
*
* @private
* @see exports.type
* @param {*} value
* @return {string}
*/
const stringify = (value) => {
var typeHint = (0, exports.canonicalType)(value);
if (!~["object", "array", "function"].indexOf(typeHint)) {
if (typeHint === "buffer") {
var json = Buffer.prototype.toJSON.call(value);
// Based on the toJSON result
return (0, exports.jsonStringify)(json.data && json.type ? json.data : json, 2).replace(/,(\n|$)/g, "$1");
}
// IE7/IE8 has a bizarre String constructor; needs to be coerced
// into an array and back to obj.
if (typeHint === "string" && typeof value === "object") {
value = value.split("").reduce(function (acc, char, idx) {
acc[idx] = char;
return acc;
}, {});
typeHint = "object";
}
else {
return (0, exports.jsonStringify)(value);
}
}
for (var prop in value) {
if (Object.prototype.hasOwnProperty.call(value, prop)) {
return (0, exports.jsonStringify)((0, exports.canonicalize)(value, null, typeHint), 2).replace(/,(\n|$)/g, "$1");
}
}
return emptyRepresentation(value, typeHint);
};
exports.stringify = stringify;
/**
* like JSON.stringify but more sense.
*
* @private
* @param {Object} object
* @param {number=} spaces
* @param {number=} depth
* @returns {*}
*/
const jsonStringify = (object, spaces, depth = 1) => {
if (typeof spaces === "undefined") {
// primitive types
return _stringify(object);
}
var space = spaces * depth;
var str = Array.isArray(object) ? "[" : "{";
var end = Array.isArray(object) ? "]" : "}";
var length = typeof object.length === "number"
? object.length
: Object.keys(object).length;
// `.repeat()` polyfill
function repeat(s, n) {
return new Array(n).join(s);
}
function _stringify(val) {
switch ((0, exports.canonicalType)(val)) {
case "null":
case "undefined":
val = "[" + val + "]";
break;
case "array":
case "object":
val = (0, exports.jsonStringify)(val, spaces, depth + 1);
break;
case "boolean":
case "regexp":
case "symbol":
case "number":
val =
val === 0 && 1 / val === -Infinity // `-0`
? "-0"
: val.toString();
break;
case "bigint":
val = val.toString() + "n";
break;
case "date":
var sDate = isNaN(val.getTime())
? val.toString()
: val.toISOString();
val = "[Date: " + sDate + "]";
break;
case "buffer":
var json = val.toJSON();
// Based on the toJSON result
json = json.data && json.type ? json.data : json;
val = "[Buffer: " + (0, exports.jsonStringify)(json, 2, depth + 1) + "]";
break;
default:
val =
val === "[Function]" || val === "[Circular]"
? val
: JSON.stringify(val); // string
}
return val;
}
for (var i in object) {
if (!Object.prototype.hasOwnProperty.call(object, i)) {
continue; // not my business
}
--length;
str +=
"\n " +
repeat(" ", space) +
(Array.isArray(object) ? "" : '"' + i + '": ') + // key
_stringify(object[i]) + // value
(length ? "," : ""); // comma
}
return (str +
// [], {}
(str.length !== 1 ? "\n" + repeat(" ", --space) + end : end));
};
exports.jsonStringify = jsonStringify;
/**
* Return a new Thing that has the keys in sorted order. Recursive.
*
* If the Thing...
* - has already been seen, return string `'[Circular]'`
* - is `undefined`, return string `'[undefined]'`
* - is `null`, return value `null`
* - is some other primitive, return the value
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
*
* @private
* @see {@link exports.stringify}
* @param {*} value Thing to inspect. May or may not have properties.
* @param {Array} [stack=[]] Stack of seen values
* @param {string} [typeHint] Type hint
* @return {(Object|Array|Function|string|undefined)}
*/
const canonicalize = (value, stack, typeHint) => {
var canonicalizedObj;
/* eslint-disable no-unused-vars */
var prop;
/* eslint-enable no-unused-vars */
typeHint = typeHint || (0, exports.canonicalType)(value);
function withStack(value, fn) {
stack.push(value);
fn();
stack.pop();
}
stack = stack || [];
if (stack.indexOf(value) !== -1) {
return "[Circular]";
}
switch (typeHint) {
case "undefined":
case "buffer":
case "null":
canonicalizedObj = value;
break;
case "array":
withStack(value, function () {
canonicalizedObj = value.map(function (item) {
return (0, exports.canonicalize)(item, stack);
});
});
break;
case "function":
/* eslint-disable-next-line no-unused-vars, no-unreachable-loop */
for (prop in value) {
canonicalizedObj = {};
break;
}
/* eslint-enable guard-for-in */
if (!canonicalizedObj) {
canonicalizedObj = emptyRepresentation(value, typeHint);
break;
}
/* falls through */
case "object":
canonicalizedObj = canonicalizedObj || {};
withStack(value, function () {
Object.keys(value)
.sort()
.forEach(function (key) {
canonicalizedObj[key] = (0, exports.canonicalize)(value[key], stack);
});
});
break;
case "date":
case "number":
case "regexp":
case "boolean":
case "symbol":
canonicalizedObj = value;
break;
default:
canonicalizedObj = value + "";
}
return canonicalizedObj;
};
exports.canonicalize = canonicalize;
/**
* @summary
* This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
* @description
* When invoking this function you get a filter function that get the Error.stack as an input,
* and return a prettify output.
* (i.e: strip Mocha and internal node functions from stack trace).
* @returns {Function}
*/
const stackTraceFilter = () => {
// TODO: Replace with `process.browser`
var is = typeof document === "undefined" ? { node: true } : { browser: true };
var slash = path_1.default.sep;
var cwd__;
if (is.node) {
cwd__ = (0, exports.cwd)() + slash;
}
else {
cwd__ = (typeof location === "undefined" ? window.location : location).href.replace(/\/[^/]*$/, "/");
slash = "/";
}
function isMochaInternal(line) {
return (~line.indexOf("node_modules" + slash + "mocha" + slash) ||
~line.indexOf(slash + "mocha.js") ||
~line.indexOf(slash + "mocha.min.js"));
}
function isNodeInternal(line) {
return (~line.indexOf("(timers.js:") ||
~line.indexOf("(events.js:") ||
~line.indexOf("(node.js:") ||
~line.indexOf("(module.js:") ||
~line.indexOf("GeneratorFunctionPrototype.next (native)") ||
false);
}
return function (stack) {
stack = stack.split("\n");
stack = stack.reduce(function (list, line) {
if (isMochaInternal(line)) {
return list;
}
if (is.node && isNodeInternal(line)) {
return list;
}
// Clean up cwd(absolute)
if (/:\d+:\d+\)?$/.test(line)) {
line = line.replace("(" + cwd__, "(");
}
list.push(line);
return list;
}, []);
return stack.join("\n");
};
};
exports.stackTraceFilter = stackTraceFilter;
/**
* Crude, but effective.
* @public
* @param {*} value
* @returns {boolean} Whether or not `value` is a Promise
*/
const isPromise = (value) => typeof value === "object" &&
value !== null &&
typeof value.then === "function";
exports.isPromise = isPromise;
/**
* Clamps a numeric value to an inclusive range.
*
* @param {number} value - Value to be clamped.
* @param {number[]} range - Two element array specifying [min, max] range.
* @returns {number} clamped value
*/
const clamp = (value, range) => Math.min(Math.max(value, range[0]), range[1]);
exports.clamp = clamp;
/**
* It's a noop.
* @public
*/
const noop = function () { };
exports.noop = noop;
/**
* Creates a map-like object.
*
* @description
* A "map" is an object with no prototype, for our purposes. In some cases
* this would be more appropriate than a `Map`, especially if your environment
* doesn't support it. Recommended for use in Mocha's public APIs.
*
* @public
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Custom_and_Null_objects|MDN:Map}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Custom_and_Null_objects|MDN:Object.assign}
* @param {...*} [obj] - Arguments to `Object.assign()`.
* @returns {Object} An object with no prototype, having `...obj` properties
*/
const createMap = function (obj) {
return Object.assign.apply(null, [Object.create(null)].concat(Array.prototype.slice.call(arguments)));
};
exports.createMap = createMap;
/**
* Creates a read-only map-like object.
*
* @description
* This differs from {@link module:utils.createMap createMap} only in that
* the argument must be non-empty, because the result is frozen.
*
* @see {@link module:utils.createMap createMap}
* @param {...*} [obj] - Arguments to `Object.assign()`.
* @returns {Object} A frozen object with no prototype, having `...obj` properties
* @throws {TypeError} if argument is not a non-empty object.
*/
const defineConstants = (obj) => {
if ((0, exports.canonicalType)(obj) !== "object" || !Object.keys(obj).length) {
throw new TypeError("Invalid argument; expected a non-empty object");
}
return Object.freeze((0, exports.createMap)(obj));
};
exports.defineConstants = defineConstants;
/**
* Returns current working directory
*
* Wrapper around `process.cwd()` for isolation
* @private
*/
const cwd = function cwd() {
return process.cwd();
};
exports.cwd = cwd;
/**
* Returns `true` if Mocha is running in a browser.
* Checks for `process.browser`.
* @returns {boolean}
* @private
*/
const isBrowser = () => Boolean(process.browser);
exports.isBrowser = isBrowser;
/**
* Casts `value` to an array; useful for optionally accepting array parameters
*
* It follows these rules, depending on `value`. If `value` is...
* 1. `undefined`: return an empty Array
* 2. `null`: return an array with a single `null` element
* 3. Any other object: return the value of `Array.from()` _if_ the object is iterable
* 4. otherwise: return an array with a single element, `value`
* @param {*} value - Something to cast to an Array
* @returns {Array<*>}
*/
const castArray = (value) => {
if (value === undefined) {
return [];
}
if (value === null) {
return [null];
}
if (typeof value === "object" &&
(typeof value[Symbol.iterator] === "function" ||
value.length !== undefined)) {
return Array.from(value);
}
return [value];
};
exports.castArray = castArray;
exports.constants = (0, exports.defineConstants)({
MOCHA_ID_PROP_NAME,
});
/**
* Creates a new unique identifier
* @returns {string} Unique identifier
*/
const uniqueID = () => (0, non_secure_1.nanoid)();
exports.uniqueID = uniqueID;
const assignNewMochaID = (obj) => {
const id = (0, exports.uniqueID)();
Object.defineProperty(obj, MOCHA_ID_PROP_NAME, {
get() {
return id;
},
});
return obj;
};
exports.assignNewMochaID = assignNewMochaID;
/**
* Retrieves a Mocha ID from an object, if present.
* @param {*} [obj] - Object
* @returns {string|void}
*/
const getMochaID = (obj) => obj && typeof obj === "object" ? obj[MOCHA_ID_PROP_NAME] : undefined;
exports.getMochaID = getMochaID;
//# sourceMappingURL=utils.js.map