// ...................................... //// compose // ...................................... const def = (x) => typeof x !== 'undefined' && x !== null; const removeSpace = (str) => str.split(/\s+/).join(''); const isFunc = (x) => def(x) && typeof x === 'function'; const isPlainObject = (x) => x !== null && typeof x === 'object' && x.constructor.name === Object.name && !('props' in x && x.$$typeof); const isFalsish = (x) => x === undefined || x === null || x === false || x === ''; //--- Memoize const memoize = (func, src) => { const cache = {}; return (...args) => { const key = JSON.stringify(args); if (!cache[key]) { cache[key] = func(...args); } console.log('memodized...', `[ ${src} ]`); return cache[key]; }; }; const unitlessKeys = { animationIterationCount: 1, aspectRatio: 1, borderImageOutset: 1, borderImageSlice: 1, borderImageWidth: 1, boxFlex: 1, boxFlexGroup: 1, boxOrdinalGroup: 1, columnCount: 1, columns: 1, flex: 1, flexGrow: 1, flexPositive: 1, flexShrink: 1, flexNegative: 1, flexOrder: 1, gridRow: 1, gridRowEnd: 1, gridRowSpan: 1, gridRowStart: 1, gridColumn: 1, gridColumnEnd: 1, gridColumnSpan: 1, gridColumnStart: 1, msGridRow: 1, msGridRowSpan: 1, msGridColumn: 1, msGridColumnSpan: 1, fontWeight: 1, lineHeight: 1, opacity: 1, order: 1, orphans: 1, tabSize: 1, widows: 1, zIndex: 1, zoom: 1, WebkitLineClamp: 1, // SVG-related properties fillOpacity: 1, floodOpacity: 1, stopOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeMiterlimit: 1, strokeOpacity: 1, strokeWidth: 1, }; const isUpper = (c) => c >= 'A' && c <= 'Z'; // ...................................... //// add Unit If Needed // ...................................... function addUnitIfNeeded(name, value) { if (value == null || typeof value === 'boolean' || value === '') { return ''; } if ( typeof value === 'number' && value !== 0 && !(name in unitlessKeys) && !name.startsWith('--') ) { return `${value}px`; } return String(value).trim(); } // ...................................... //// hyphenate // ...................................... function hyphenate(string) { let output = ''; for (let i = 0; i < string.length; i++) { const c = string[i]; // Check for CSS variable prefix if (i === 1 && c === '-' && string[0] === '-') { return string; } if (isUpper(c)) { output += '-' + c.toLowerCase(); } else { output += c; } } return output.startsWith('ms-') ? '-' + output : output; } // ...................................... //// obj To CssString // ...................................... const objToCssString = (obj) => { const rules = []; for (const key in obj) { const val = obj[key]; if (!obj.hasOwnProperty(key) || isFalsish(val)) continue; if ((Array.isArray(val) ) || isFunc(val)) { rules.push(`${hyphenate(key)}:`, val, ';'); } else if (isPlainObject(val)) { rules.push(`${key} {`, ...objToCssString(val), '}'); } else { rules.push(`${hyphenate(key)}: ${addUnitIfNeeded(key, val)};`); } } return rules.join(''); }; function murmur2(str) { if (!str) return '0'; var h = 0; // Mix 4 bytes at a time into the hash var k, i = 0, len = str.length; for (; len >= 4; ++i, len -= 4) { k = (str.charCodeAt(i) & 0xff) | ((str.charCodeAt(++i) & 0xff) << 8) | ((str.charCodeAt(++i) & 0xff) << 16) | ((str.charCodeAt(++i) & 0xff) << 24); k = /* Math.imul(k, m): */ (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16); k ^= /* k >>> r: */ k >>> 24; h = /* Math.imul(k, m): */ ((k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)) ^ /* Math.imul(h, m): */ ((h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16)); } // Handle the last few bytes of the input array switch (len) { case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16; case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8; case 1: h ^= str.charCodeAt(i) & 0xff; h = /* Math.imul(h, m): */ (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16); } // Do a few final mixes of the hash to ensure the last few // bytes are well-incorporated. h ^= h >>> 13; h = /* Math.imul(h, m): */ (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16); return ((h ^ (h >>> 15)) >>> 0).toString(36); } // ...................................... //// createHash // ...................................... const _createHash = (string) => { let charArray = string.split(''); const hash = charArray.reduce((hash, char) => { return hash + char.charCodeAt(0) || hash; }, 0); return hash < 0 ? (hash * -1).toString() : hash.toString(); }; const hashInObject = (value) => { const hashKey = _createHash(removeSpace(Object.keys(value).join(''))); const hashValue = _createHash(removeSpace(Object.values(value).join(''))); const hash = `${hashKey}-${hashValue}`; return hash.toString(); }; const completeHash = (value) => { if (typeof value === 'object') { const hash = hashInObject(value); const target = objToCssString(value); return { uniqueKey: murmur2(removeSpace(target)), context: hash, value, }; } const hash = hashInObject({ css: value }); const target = value; return { uniqueKey: murmur2(removeSpace(target)), context: hash, value, }; }; const createHashMemoized = memoize((string) => { return murmur2(string); }, 'hash'); // ...................................... //// createHash // ...................................... const createHash = (value, opt = false) => { if (!value) { return ''; } if (!opt) { const string = typeof value === 'string' ? value : objToCssString(value); return createHashMemoized(removeSpace(string)); } return completeHash(value); }; export { createHash };