import { VALID_ID_PREFIX, NULL_BYTE_PLACEHOLDER } from './constants.js'; // port from nanoid // https://github.com/ai/nanoid const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; function nanoid(size = 21) { let id = ""; let i = size; while (i--) { id += urlAlphabet[Math.random() * 64 | 0]; } return id; } const RealDate = Date; function random(seed) { const x = Math.sin(seed++) * 1e4; return x - Math.floor(x); } function shuffle(array, seed = RealDate.now()) { let length = array.length; while (length) { const index = Math.floor(random(seed) * length--); const previous = array[length]; array[length] = array[index]; array[index] = previous; ++seed; } return array; } /** * Get original stacktrace without source map support the most performant way. * - Create only 1 stack frame. * - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms). */ function createSimpleStackTrace(options) { const { message = "$$stack trace error", stackTraceLimit = 1 } = options || {}; const limit = Error.stackTraceLimit; const prepareStackTrace = Error.prepareStackTrace; Error.stackTraceLimit = stackTraceLimit; Error.prepareStackTrace = (e) => e.stack; const err = new Error(message); const stackTrace = err.stack || ""; Error.prepareStackTrace = prepareStackTrace; Error.stackTraceLimit = limit; return stackTrace; } function notNullish(v) { return v != null; } function assertTypes(value, name, types) { const receivedType = typeof value; const pass = types.includes(receivedType); if (!pass) { throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`); } } function isPrimitive(value) { return value === null || typeof value !== "function" && typeof value !== "object"; } function slash(path) { return path.replace(/\\/g, "/"); } const postfixRE = /[?#].*$/; function cleanUrl(url) { return url.replace(postfixRE, ""); } const externalRE = /^(?:[a-z]+:)?\/\//; const isExternalUrl = (url) => externalRE.test(url); /** * Prepend `/@id/` and replace null byte so the id is URL-safe. * This is prepended to resolved ids that are not valid browser * import specifiers by the importAnalysis plugin. */ function wrapId(id) { return id.startsWith(VALID_ID_PREFIX) ? id : VALID_ID_PREFIX + id.replace("\0", NULL_BYTE_PLACEHOLDER); } /** * Undo {@link wrapId}'s `/@id/` and null byte replacements. */ function unwrapId(id) { return id.startsWith(VALID_ID_PREFIX) ? id.slice(VALID_ID_PREFIX.length).replace(NULL_BYTE_PLACEHOLDER, "\0") : id; } function withTrailingSlash(path) { if (path.at(-1) !== "/") { return `${path}/`; } return path; } const bareImportRE = /^(?![a-z]:)[\w@](?!.*:\/\/)/i; function isBareImport(id) { return bareImportRE.test(id); } function toArray(array) { if (array === null || array === undefined) { array = []; } if (Array.isArray(array)) { return array; } return [array]; } function isObject(item) { return item != null && typeof item === "object" && !Array.isArray(item); } function isFinalObj(obj) { return obj === Object.prototype || obj === Function.prototype || obj === RegExp.prototype; } function getType(value) { return Object.prototype.toString.apply(value).slice(8, -1); } function collectOwnProperties(obj, collector) { const collect = typeof collector === "function" ? collector : (key) => collector.add(key); Object.getOwnPropertyNames(obj).forEach(collect); Object.getOwnPropertySymbols(obj).forEach(collect); } function getOwnProperties(obj) { const ownProps = new Set(); if (isFinalObj(obj)) { return []; } collectOwnProperties(obj, ownProps); return Array.from(ownProps); } const defaultCloneOptions = { forceWritable: false }; function deepClone(val, options = defaultCloneOptions) { const seen = new WeakMap(); return clone(val, seen, options); } function clone(val, seen, options = defaultCloneOptions) { let k, out; if (seen.has(val)) { return seen.get(val); } if (Array.isArray(val)) { out = Array.from({ length: k = val.length }); seen.set(val, out); while (k--) { out[k] = clone(val[k], seen, options); } return out; } if (Object.prototype.toString.call(val) === "[object Object]") { out = Object.create(Object.getPrototypeOf(val)); seen.set(val, out); // we don't need properties from prototype const props = getOwnProperties(val); for (const k of props) { const descriptor = Object.getOwnPropertyDescriptor(val, k); if (!descriptor) { continue; } const cloned = clone(val[k], seen, options); if (options.forceWritable) { Object.defineProperty(out, k, { enumerable: descriptor.enumerable, configurable: true, writable: true, value: cloned }); } else if ("get" in descriptor) { Object.defineProperty(out, k, { ...descriptor, get() { return cloned; } }); } else { Object.defineProperty(out, k, { ...descriptor, value: cloned }); } } return out; } return val; } function noop() {} function objectAttr(source, path, defaultValue = undefined) { // a[3].b -> a.3.b const paths = path.replace(/\[(\d+)\]/g, ".$1").split("."); let result = source; for (const p of paths) { result = new Object(result)[p]; if (result === undefined) { return defaultValue; } } return result; } function createDefer() { let resolve = null; let reject = null; const p = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); p.resolve = resolve; p.reject = reject; return p; } /** * If code starts with a function call, will return its last index, respecting arguments. * This will return 25 - last ending character of toMatch ")" * Also works with callbacks * ``` * toMatch({ test: '123' }); * toBeAliased('123') * ``` */ function getCallLastIndex(code) { let charIndex = -1; let inString = null; let startedBracers = 0; let endedBracers = 0; let beforeChar = null; while (charIndex <= code.length) { beforeChar = code[charIndex]; charIndex++; const char = code[charIndex]; const isCharString = char === "\"" || char === "'" || char === "`"; if (isCharString && beforeChar !== "\\") { if (inString === char) { inString = null; } else if (!inString) { inString = char; } } if (!inString) { if (char === "(") { startedBracers++; } if (char === ")") { endedBracers++; } } if (startedBracers && endedBracers && startedBracers === endedBracers) { return charIndex; } } return null; } function isNegativeNaN(val) { if (!Number.isNaN(val)) { return false; } const f64 = new Float64Array(1); f64[0] = val; const u32 = new Uint32Array(f64.buffer); const isNegative = u32[1] >>> 31 === 1; return isNegative; } function toString(v) { return Object.prototype.toString.call(v); } function isPlainObject(val) { return toString(val) === "[object Object]" && (!val.constructor || val.constructor.name === "Object"); } function isMergeableObject(item) { return isPlainObject(item) && !Array.isArray(item); } /** * Deep merge :P * * Will merge objects only if they are plain * * Do not merge types - it is very expensive and usually it's better to case a type here */ function deepMerge(target, ...sources) { if (!sources.length) { return target; } const source = sources.shift(); if (source === undefined) { return target; } if (isMergeableObject(target) && isMergeableObject(source)) { Object.keys(source).forEach((key) => { const _source = source; if (isMergeableObject(_source[key])) { if (!target[key]) { target[key] = {}; } deepMerge(target[key], _source[key]); } else { target[key] = _source[key]; } }); } return deepMerge(target, ...sources); } export { assertTypes, cleanUrl, clone, createDefer, createSimpleStackTrace, deepClone, deepMerge, getCallLastIndex, getOwnProperties, getType, isBareImport, isExternalUrl, isNegativeNaN, isObject, isPrimitive, nanoid, noop, notNullish, objectAttr, shuffle, slash, toArray, unwrapId, withTrailingSlash, wrapId };