aveazul
Version:
Bluebird drop-in replacement built on native Promise
213 lines (188 loc) • 5.98 kB
JavaScript
;
/**
* Determines if a function is a class (either ES6 class or ES5 constructor function)
* This function performs several checks to identify different class patterns:
* 1. ES6 classes with the 'class' keyword
* 2. Constructor functions (ES5 classes) with prototype methods
*
* @param {*} fn - The value to check
* @returns {boolean} - True if the function is a class, false otherwise
*/
const thisAssignmentPattern = /this\s*\.\s*\S+\s*=/;
function isClass(fn) {
try {
if (typeof fn === "function") {
const keys = Object.getOwnPropertyNames(fn.prototype);
const fnStr = fn.toString();
const es6Class = fnStr.startsWith("class ") || /^class\s+/.test(fnStr);
const hasMethods = keys.length > 1;
const hasMethodsOtherThanConstructor =
keys.length > 0 && !(keys.length === 1 && keys[0] === "constructor");
const hasThisAssignmentAndStaticMethods =
thisAssignmentPattern.test(fnStr) &&
Object.getOwnPropertyNames(fn).length > 0;
if (
es6Class ||
hasMethods ||
hasMethodsOtherThanConstructor ||
hasThisAssignmentAndStaticMethods
) {
return true;
}
}
return false;
} catch (e) {
return false;
}
}
const rident = /^[a-z$_][a-z$_0-9]*$/i;
function isIdentifier(str) {
return rident.test(str);
}
function isConstructor(func) {
if (!func) {
return false;
}
const proto = func.prototype;
return (
!!proto &&
!!proto.constructor &&
!!proto.constructor.name &&
proto.constructor.name === func.name
);
}
/**
* Prop filtering code copied from bluebird/js/release
*/
const noCopyProps = [
"arity",
"length",
"name",
"arguments",
"caller",
"callee",
"prototype",
"__isPromisified__",
];
const noCopyPropsPattern = new RegExp("^(?:" + noCopyProps.join("|") + ")$");
function propsFilter(key) {
return !noCopyPropsPattern.test(key);
}
function copyOwnProperties(source, target, filter = propsFilter) {
const names = Object.getOwnPropertyNames(source);
for (const name of names) {
if (filter(name)) {
Object.defineProperty(
target,
name,
Object.getOwnPropertyDescriptor(source, name)
);
}
}
}
/**
* Copied from bluebird/js/release/util.js
* @param {*} fn
* @returns {boolean}
*/
function isPromisified(fn) {
try {
return fn.__isPromisified__ === true;
} catch (e) {
return false;
}
}
/**
* Determines if an object is a Promise instance
* @param {*} obj - The object to check
* @returns {boolean} - True if the object is a Promise instance, false otherwise
*/
function isPromise(obj) {
return (
obj instanceof Promise ||
(obj != null &&
typeof obj === "object" &&
typeof obj.then === "function" &&
typeof obj.catch === "function")
);
}
// istanbul ignore next
const emptyFatArrow = () => {};
// istanbul ignore next
const emptyFunction = function () {};
const defaultExcluded = [
Object.getPrototypeOf(Array), // Array.prototype
Object.getPrototypeOf(Object), // Object.prototype
Object.getPrototypeOf(Function), // Function.prototype
Object.getPrototypeOf([]),
Object.getPrototypeOf({}),
Object.getPrototypeOf(emptyFatArrow),
Object.getPrototypeOf(emptyFunction),
];
function isExcludedPrototype(proto) {
return defaultExcluded.includes(proto);
}
/**
* Gets all property keys from an object and its prototype chain, excluding standard
* prototypes like Object.prototype, Array.prototype, and Function.prototype
*
* @param {Object} target - The target object to get keys from
* @param {Array} [excludedPrototypes=[]] - An array of prototype objects to exclude keys from
* @returns {Array<string>} - Array of property keys
*/
function getObjectKeys(target, excludedPrototypes = []) {
const excluded =
excludedPrototypes.length > 0 ? excludedPrototypes : defaultExcluded;
// Get own properties
const ownKeys = Object.getOwnPropertyNames(target);
// Get prototype properties, excluding those from excluded prototypes
let protoKeys = [];
let currentProto = Object.getPrototypeOf(target);
// Walk up the prototype chain until we hit null or an excluded prototype
while (currentProto && !excluded.includes(currentProto)) {
protoKeys = [...protoKeys, ...Object.getOwnPropertyNames(currentProto)];
currentProto = Object.getPrototypeOf(currentProto);
}
// Combine own properties and prototype properties
return [...protoKeys, ...ownKeys];
}
/**
* Triggers an uncaught exception in a safe way by scheduling it on the next event loop tick
* This is used for fatal errors that should crash the process
* @param {Error} error - The error to throw
*/
function triggerUncaughtException(error) {
if (!(error instanceof Error)) {
error = new Error(String(error));
}
// Use setTimeout with 0ms delay to throw on the next event loop tick
// This ensures the current execution context completes first
setTimeout(() => {
throw error;
}, 0);
}
function toArray(args) {
if (!Array.isArray(args)) {
// Check if args is iterable
if (args != null && typeof args[Symbol.iterator] === "function") {
// Convert iterable to array, must do this to get the length, in order
// to detect if too many errors occurred and completion is impossible.
args = Array.from(args);
} else {
throw new TypeError(
"expecting an array or an iterable object but got " + args
);
}
}
return args;
}
module.exports.copyOwnProperties = copyOwnProperties;
module.exports.isClass = isClass;
module.exports.isIdentifier = isIdentifier;
module.exports.isConstructor = isConstructor;
module.exports.isPromisified = isPromisified;
module.exports.isPromise = isPromise;
module.exports.triggerUncaughtException = triggerUncaughtException;
module.exports.getObjectKeys = getObjectKeys;
module.exports.isExcludedPrototype = isExcludedPrototype;
module.exports.toArray = toArray;