// ...................................... //// compose // ...................................... const compose = (...fns) => (arg) => fns.reduceRight((acc, fn) => (fn ? fn(acc) : acc), arg); // ...................................... //// partition // ...................................... const accept = ([x, ...xs], fn, index = 0) => { if (undef(x)) return []; if (fn(x, index)) { return [x, ...accept(xs, fn, (index += 1))]; } else { return [...accept(xs, fn, (index += 1))]; } }; const reject = ([x, ...xs], fn, index = 0) => { if (undef(x)) return []; if (!fn(x, index)) { return [x, ...reject(xs, fn, (index += 1))]; } else { return [...reject(xs, fn, (index += 1))]; } }; // ...................................... //// partition // ...................................... const partition = (xs, fn) => [accept(xs, fn), reject(xs, fn)]; const def = (x) => typeof x !== 'undefined' && x !== null; const undef = (x) => !def(x); //--- pattern const labelPattern = /label:\s*([^\s;\n{]+)\s*(;|$)/g; const lineBreakPattern = /(\r\n|\n|\r)/gm; const removeCommentPattern = /\/\*[\s\S]*?\*\//g; const inLineCommentsPattern = /(\/\/).*?__\$__/gm; //--- regex const newRegexExp = (pattern, flag = '') => { const regex = new RegExp(pattern, flag); regex.lastIndex = 0; return regex; }; const regExp = { regex: (pattern, flag) => newRegexExp(pattern, flag), match: (str, pattern, flag) => str.match(regExp.regex(pattern, flag)) || [], test: (str, pattern, flag) => regExp.regex(pattern, flag).test(str), exec: (str, pattern, flag) => regExp.regex(pattern, flag).exec(str) || [], }; //--- object const ownkey = (obj) => Object.keys(obj)[0]; const ownValue = (obj) => Object.values(obj)[0]; const arrayOwnProperties = (obj) => isArray(obj) ? obj : Object.keys(obj).map((m) => ({ [m]: obj[m] })); //--- remove const removeDoubleSpace = (x) => isArray(x) ? x.map((str) => str.replace(/\s{2,}/g, ' ')) : x.replace(/\s{2,}/g, ' '); const removeLabel = (str) => str.replace(labelPattern, ''); const removeSpace = (str) => str.split(/\s+/).join(''); const removeLineBreak = (str) => str.replaceAll(lineBreakPattern, ''); //--- is const isArray = (x) => Array.isArray(x); const isObject = (x) => x != null && !Array.isArray(x) && typeof x === 'object'; 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 isBrowser$2 = typeof document !== 'undefined'; const insertDocument = (cache) => { const date = new Date(); cache.document = { isBrowser: isBrowser$2, environment: isBrowser$2 ? 'client' : 'server', created_at: date.toLocaleString('pt-BR', { timeZone: 'America/Sao_Paulo', }), id: `${cache.key}-${date.getTime() + Math.random()}`, }; }; //--- const stylesToCssString = (styles) => { return styles.reduce((acc, prev) => { const key = ownkey(prev); const classname = key.startsWith('.') ? key : `.${key}`; const value = ownValue(prev); const css = ` ${classname} { ${removeLabel(value)} }`; acc += css; return acc.trim(); }, ''); }; const registerStyles = (cache, serialized) => { if (!serialized.styles) { return cache; } //--- 'serialized.name' contains classnameHash cache.registered = { ...cache.registered, [serialized.name]: removeDoubleSpace(stylesToCssString(serialized.styles)), }; }; // ...................................... //// insert Styles // ...................................... const insertStyles = (cache, serialized) => { const name = serialized.name; const propKey = name.startsWith('.') ? name.slice(1) : name; const classnameHash = propKey.replace(`${cache.key}-`, ''); serialized.styles.forEach((element) => { const key = ownkey(element); const value = ownValue(element); cache.insert(cache, { name: classnameHash, styles: { [key]: value }, }); }); registerStyles(cache, { name: classnameHash, styles: serialized.styles, }); insertDocument(cache); return cache; }; // ...................................... //// insert Styles Server // ...................................... const insertStylesServer = (cache, serialized) => { let insert = []; registerStyles(cache, serialized); serialized.styles.forEach((element) => { const key = Object.keys(element)[0]; insert.push({ name: key.startsWith('.') ? key.slice(1) : key, styles: element, }); }); insertDocument(cache); const _cache = cache.insert(cache, { name: serialized.name, styles: insert, }); return _cache; }; /*---e.g. ["elpro", "_E_", ".elpro-165cwbw", "elpro-165cwbw" ]*/ const getClassnameHash = (attr) => { const arr = attr.split(' '); const key = arr[0]; const str = arr[2] || arr[1]; const hash = str .replace(`${key}-`, '') .match(/[0-9a-z]+/)[0] .trim(); return hash; }; // ...................................... //// Global cache // ...................................... //--- Global cache let insertedStyled = isBrowser$2 ? {} : []; let insertedCss = isBrowser$2 ? {} : []; // ...................................... //// processCreateCacheStyle // ...................................... const processCreateCacheStyle = (key, inserted, src) => { let registered = {}; let insert; let compact = false; const nodesToHydrate = []; if (isBrowser$2) { const ssrStyles = document.querySelectorAll( `style[data-e-style^="${key} "]` ); let classnameHash = undefined; Array.prototype.forEach.call(ssrStyles, (node, i) => { const dataAttribute = node.getAttribute('data-e-style'); const cssText = node.innerHTML; classnameHash = getClassnameHash(dataAttribute); //--- for 'Process Instance' to identify whether it exists in cache or not registered[classnameHash] = registered[classnameHash] ? (registered[classnameHash] += ` ${cssText}`) : ` ${cssText}`; registered[classnameHash] = registered[classnameHash].trim(); inserted = { ...inserted, [classnameHash]: true }; compact = true; nodesToHydrate.push(node); }); // client insert = (cache, serialized) => { if (!cache.inserted[serialized.name]) { compact = true; inserted = { ...cache.inserted, [serialized.name]: true }; cache.inserted = inserted; //--- saving to global cache if (key === 'css') { insertedCss = cache.inserted; } else { insertedStyled = cache.inserted; } return cache; } }; } else { // server insert = (cache, serialized) => { if (!cache.compact) { //--- prevents duplicate insertion const hasCach = cache.inserted.findIndex( (elel) => elel.name === serialized.name ); if (hasCach >= 0) { return cache; } inserted = serialized.styles.flat(); cache.inserted = [...cache.inserted, ...inserted]; //--- saving to global cache if (key === 'css') { insertedCss = cache.inserted; } else { insertedStyled = cache.inserted; } return cache; } }; } const cache = { src, document: {}, key: key, compact, sheet: { tags: nodesToHydrate }, registered: registered, inserted: inserted, insert, }; insertDocument(cache); return cache; }; // ...................................... //// createCacheStyle // ...................................... const createCacheStyle = (options, src) => { const key = options && options.key ? options.key : 'css'; return src === '_CSS' ? processCreateCacheStyle(key, insertedCss, src) : processCreateCacheStyle(key, insertedStyled, src); }; 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, }; // ...................................... //// critical Decl Char // ...................................... /*--- prevents replacing class names with colons e.g. &.classname p:not(.classic) */ const preventClassnameColons = (str) => { const index = str.indexOf('{'); const noReplaceable = str.substring(0, index); const replaceable = str.substring(index); return `${noReplaceable}${replaceable .replaceAll(':', ': ') .replaceAll(';', '; ')}`; }; const replacementCriticalDecl = (match, p1, p2, p3) => { if (match.includes('{')) { return preventClassnameColons(match); } return match.replaceAll(':', ': ').replaceAll(';', '; '); }; // ...................................... //// critical Class Char // ...................................... const criticalDeclChar = (str) => { return str.replaceAll(/[a-z\-\s]+:.*?;/gm, replacementCriticalDecl); }; // ...................................... //// critical Class Char // ...................................... const criticalClassChar = (str) => { return str .replaceAll(/\{|\}/g, ' $& ') .replaceAll('&', ' &'); }; // ...................................... //// sanitize Critical Chars // ...................................... const sanitizeCriticalChars = (str) => { const string = removeDoubleSpace( removeLineBreak(str).trim() ); return criticalDeclChar(criticalClassChar(string)); }; // ...................................... //// prettify Attributes // ...................................... const attributePattern = /\(.*?\)|\[.*?\]/gm; const replacementPrettifyAttrs = (match, p1, p2) => { if (match.includes('[')) { return removeSpace(match); //--- match.includes('(') } else { //--- hyphenated compound words e.g. min-width: if (regExp.test(match, /[a-z]-[a-z]/gm)) { return removeSpace(match); } // no replace return match; } }; /*--- prevents substitution in case of declaration value e.g. translate3d(0, 100, 0) , calc(100% - 30px) and in case of classes with attributes or atrules e.g. @media (min-width:760px) */ const prettifyAttributes = (str) => { return str.replaceAll(attributePattern, replacementPrettifyAttrs); }; // ...................................... //// prettify Atrules // ...................................... const replacementPrettifyAtrules = (match, p1, p2) => { return removeSpace(match).replace('{', ' {') }; const prettifyAtrules = (str) => { return str.replaceAll(/@.*?\{/gm, replacementPrettifyAtrules); }; // ...................................... //// prettify // ...................................... const prettify = (str) => { const string = removeDoubleSpace(str) .replaceAll(' ;', ';') .replaceAll(' :', ':') .replaceAll(' (', '(') .replaceAll('( ', '(') .replaceAll(' )', ')') .replaceAll(' [', '[') .replaceAll('[ ', '[') .replaceAll(' ]', ']') .replaceAll(/(;){1,}/gm, '; ') .replaceAll(/;\s{1,};/gm, '; ') .replaceAll('};', '}'); const attrPrettified = prettifyAttributes(string); const atrulesPrettified =prettifyAtrules(attrPrettified); return removeDoubleSpace(atrulesPrettified).trim(); }; // ...................................... //// sanitizeString // ...................................... const sanitizeString = (str) => { const sanitized = sanitizeCriticalChars(str); const string = prettify(sanitized); return string }; // ...................................... //// sanitize // ...................................... const sanitize = (x) => { if (!x) { return isArray(x) ? [] : ''; } return isArray(x) ? x.map((_x) => sanitizeString(_x)) : sanitizeString(x); }; const parseCssArray = (fn) => ([head, ...tail], result = []) => { let index = 0; if (!head) { return result; } let array = [head, ...tail]; const current = head; //.trim() const next = tail[0]; const [cssString, nextArray] = fn(current, next, index, array); // console.log({ current, next, array, cssString, nextArray }); result.push(cssString); tail = nextArray ? nextArray : tail; return parseCssArray(fn)(tail, result); }; // ...................................... //// accumulateMultValues // ...................................... const accumulateMultValues = ([head, ...tail], fn, result = []) => { if (!head) { return result; } if (fn(head)) { return accumulateMultValues([], fn, [...result, head]); } return accumulateMultValues(tail, fn, [...result, head]); }; // ...................................... //// declFn // ...................................... const filterArray = (index, limiter, array) => { return array.filter((_, i) => i < index || i > index + limiter); }; /*--- declarations with multiple values e.g. border: 2mm rgba(211, 220,50,.8);*/ const declFn = (current, next, index, array) => { if (next && current.endsWith(':')) { const slice = array.slice(index + 1); const accumulated = accumulateMultValues(slice, (x) => x.trim().endsWith(';') ); const cssString = `${current} ${accumulated.join(' ')}`; const filteredArray = filterArray(index, accumulated.length, array); return [cssString, filteredArray]; } return [current]; }; // ...................................... //// classnamesFn // ...................................... /*--- classnames with multiple names e.g. .class-1 .class-2 , element attribules .... || @keyframes */ const classnamesFn = (current, next, index, array) => { //--- ignore atrules for example @media because it doesn't have multiple classes if ( current.startsWith('.') || current.startsWith('&.') || current.startsWith('&:') || current.includes('@') ) { if (next && next !== '{') { const slice = array.slice(index + 1); const accumulated = accumulateMultValues(slice, (x) => x.trim().endsWith('{') ).slice(0, -1); const cssString = `${current} ${accumulated.join(' ')}`; const filteredArray = filterArray(index, accumulated.length, array); return [cssString, filteredArray]; } //--- single clasname return [current]; } return [current]; }; // ...................................... //// stringToArray // ...................................... const stringToArray = (str) => str .split(' ') .filter(Boolean) .filter((f) => !f.includes('undefined')); // ...................................... //// sanitizeArray // ...................................... const sanitizeArray = compose( parseCssArray(classnamesFn), parseCssArray(declFn), stringToArray ); // ...................................... //// css To Array // ...................................... const cssToArray = (str) => { return sanitizeArray(str); }; // ...................................... //// Mult Slectors // ...................................... const multSlectors = (props) => { // console.log({props}); const [classnameRoot, nameRoot, ...ignore] = props; //--- multiSelectors if (nameRoot.includes(',')) { const split = nameRoot.split(','); const [h, ...t] = split; const replaceH = h.includes('&') ? `${h.replaceAll('&', classnameRoot)}` : `${classnameRoot} ${h}`; const replaceT = t.map((m) => m.includes('&') ? `, ${m.replaceAll('&', classnameRoot)}` : `, ${classnameRoot} ${m}` ); const multiSelectors = removeDoubleSpace( `${replaceH}${replaceT.join(' ')}` ).trim(); return multiSelectors; } }; // ...................................... //// replace Props Mult Slectors // ...................................... const replacePropsMultSlectors = (props) => { const [propsRoot, propsMult] = partition( props, (elel, indx) => !elel.includes(',') ); const propsRootReplaced = replaceProps.sigleSelectors(propsRoot).join(' '); const propsMultReplaced = [multSlectors([propsRootReplaced, ...propsMult])]; return [propsRootReplaced, propsMultReplaced]; }; // ...................................... //// replace Props // ...................................... let count = 0; const sigleSelectors = (props, result = '') => { count += 1; if (props.length === 1) { return props; } const [a, b, ...tail] = props; if (count === 20 || !a || !b) { return [result]; } const replace = b.includes('&') ? b.replaceAll('&', a) : `${a} ${b}`; return sigleSelectors([replace, ...tail], replace); }; // ...................................... //// replace Props // ...................................... const replaceSigleSelectors = (props) => { return sigleSelectors(props); }; const replaceProps = { sigleSelectors: (props) => replaceSigleSelectors(props), multSlectors: (props) => replacePropsMultSlectors(props), }; // ...................................... //// replaceName // ...................................... const replaceName = (name, classname) => { const ampersandName = Array.isArray(classname) ? classname.join(' ') : classname; if (name.includes('&:')) { return `${ampersandName.replaceAll('&', '')}${name.replace( '&', '' )}`.trim(); } return name.replaceAll('&', '').trim(); }; // ...................................... //// extract To Chunks // ...................................... const extractToChunks = (array, fn) => { let nameIndex = 0; let name = undefined; let open = []; let close = []; let closure = false; let indexArray = 0; array.forEach((element, index) => { if (!closure) { if (fn(element, index, array)) { nameIndex = index; name = element; } if (name) { if (element.includes('{')) { open.push(index); } if (element.includes('}')) { close.push(index); } } //--- closure class if (open.length > 0) { if (open.length === close.length) { closure = true; indexArray = index; } } } }); open = open[0]; close = close[close.length - 1]; return { array, index: indexArray, name, nameIndex, open, close, props: [name], value: array.filter((_, index) => index > open && index < close), remaining: array.filter((_, index) => index < nameIndex || index > close), }; }; // ...................................... //// create Tokens // ...................................... const createTokens = (array, indexArray = -1, result = [], cssArray) => { indexArray += 1; const [head, ...tail] = array; if (!head) { return result; } const next = tail[0] ? tail[0] : ''; if (next.includes('{')) { const classname = head; const chunks = extractToChunks( array, (element, index, array) => element === classname ); result = [...result, chunks]; return createTokens(chunks.remaining, -1, result); } return createTokens(tail, indexArray, result); }; // ...................................... //// tokenizer // ...................................... const tokenizer = (cssArray) => { const tokens = createTokens(cssArray, -1, []); return tokens; }; // ...................................... //// hasChildren // ...................................... const hasChildren = (obj) => { return obj.children.length ? true : false; }; const tokenizerRootChildrenChild = (child, props, result = []) => { const value = child.value; const tokenized = tokenizer(value); const rootChildren = createRoot(tokenized, value, props); props = [...props, child.name].flat(); //--- children if (hasChildren(rootChildren)) { return tokenized .map((m) => { return tokenizerRootChildrenChild( m, props, [ ...result, { ...rootChildren, type: '', name: child.name, props: props, }, ].flat() ); }) .flat(); } else { //--- root return [ ...result.flat(), { ...rootChildren, type: '', name: child.name, props: props, }, ].flat(); } }; const getNameAtrules = (props) => { return props.filter((f) => f.includes('@'))[0]; }; // ...................................... //// tokenizerRootChildren // ...................................... const tokenizerRootChildren = (child, tokenized, rootChildren) => { const children = tokenized .map((m) => tokenizerRootChildrenChild(m, rootChildren.props)) .flat(); return [ ...children.map((child) => { const props = child.props.flat(); const name = props.join(' ').includes('@') ? getNameAtrules(props) : child.name; return { ...child, props, name: name, type: 'children-root-children', }; }), ]; }; // ...................................... //// processChildren // ...................................... const processChildren = (child, classnameRoot) => { const { value, name } = child; const classname = [`${classnameRoot}`, name]; if ( name.includes('@') && !name.includes('@media') && !name.includes('@document') ) { return [ { children: [], name: name, props: classname, //[classnameRoot], type: 'children-root-atrule', value: child.value, }, ]; } //--- root with children const tokenized = tokenizer(value); const rootChildren = createRoot(tokenized, value, classname); // console.log({child, tokenized, rootChildren, value}); if (hasChildren(rootChildren)) { const rootChildrenChild = tokenizerRootChildren( child, tokenized, rootChildren ); const props = rootChildren.props.flat(); const name = child.name; return [ { ...rootChildren, children: [], name: name, props: props, type: 'children-root', }, ...rootChildrenChild, ].flat(); } else { //--- root without children const props = rootChildren.props.flat(); const _name = child.name; return [ { ...rootChildren, name: _name, props: props, type: 'children-root', }, ].flat(); } }; // ...................................... //// value Root // ...................................... const valueRoot = (tokenized, init) => tokenized.reduce((acc, tokens) => { acc = acc.replaceAll(`${tokens.name} { ${tokens.value.join(' ')} }`, ''); return acc; }, init); // ...................................... //// create Root // ...................................... const createRoot = (tokenized, array, props) => { const init = array.join(' '); props = props.flat(); const name = props[props.length - 1]; const value = valueRoot(tokenized, init).split(' ').filter(Boolean); return { children: tokenized.map((child) => processChildren(child, name)).flat(), name: name, props: props, type: 'root', value: value, }; }; // ...................................... //// parseProps // ...................................... const parseProps = (childrens, classnameRoot) => { return childrens.reduce((acc, child) => { const propsArray = child.props.map((str) => str.trim()); if (propsArray.length === 1) { return [...acc, child]; } if (child.name.includes('@')) { const props = replaceProps.sigleSelectors( propsArray.filter((f) => !f.includes('@')) ); const name = child.name; //replaceName(child.name, classnameRoot); return [...acc, { ...child, props, name }]; } //--- mult selectors if (child.name.includes(',')) { const [propsRoot, propsMult] = replaceProps.multSlectors(propsArray); const props = propsMult; const name = replaceName(child.name, propsRoot); return [...acc, { ...child, props, name }]; } //--- sigle selectors const props = replaceProps.sigleSelectors(propsArray); const name = replaceName(child.name, classnameRoot); return [...acc, { ...child, props, name }]; }, []); }; // ...................................... //// flat Atrules // ...................................... const flatAtrules = (atrules) => { const childrenValue = atrules.reduce((acc, prev) => { const name = prev.name; const classname = prev.props.join(' '); const value = prev.value.join(' '); const str = `${classname} { ${value} } `; acc[name] = acc[name] ? (acc[name] += [...new Set([str])]) : (acc[name] = [...new Set([str])]); return acc; }, {}); return Object.entries(childrenValue).map(([name, value]) => { const atruleValue = Array.isArray(value) ? value.map((v) => v.trim()) : [value.trim()]; return { children: [], name, props: [], type: '@media', value: atruleValue, }; }); }; // ...................................... //// flat Atrules // ...................................... const compiledChildren = (children, classnameRoot) => { const compiled = parseProps(children, classnameRoot); const [atrulesClassnames, regularClassnames] = partition( compiled, (elel, indx) => elel.name.includes('@') && !elel.name.includes('@keyframes') ); return [...regularClassnames, ...flatAtrules(atrulesClassnames)]; }; // ...................................... //// compile // ...................................... const compile = (cssArray, classnameRoot) => { //--- an array of existing classes in css template strings const tokenized = tokenizer(cssArray); //--- root class created with classnameRoot const root = createRoot(tokenized, cssArray, [`.${classnameRoot}`]); //--- classes belonging to root class const childrens = compiledChildren(root.children, classnameRoot); const compiled = [{ ...root, children: [] }, ...childrens]; return compiled; }; // ...................................... //// compiled (memoize) // ...................................... const compiled = memoize((cssString, classnameRoot) => { const string = sanitize(cssString); const cssArray = cssToArray(string); const _compile = compile(cssArray, [classnameRoot]); return _compile; }, 'compile'); // ...................................... //// serialize String // ...................................... const serializeString = (cssString, classnameRoot = 'css') => { return compiled(cssString, classnameRoot); }; 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(''); }; // ...................................... //// serialize Object // ...................................... const serializeObject = (args, classnameRoot) => { return serializeString (objToCssString(args), classnameRoot) }; 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); }; // ...................................... //// prettier // ...................................... const prettierString = (str) => removeDoubleSpace(str).trim(); const prettier = (obj) => { return Object.entries(obj).reduce((acc, [key, value]) => { return { ...acc, [prettierString(key)]: prettierString(value), }; }, {}); }; const getLabelHash = (str) => { const match = str.split(' '); return match[0]; }; // ...................................... //// resolver Atrules // ...................................... // const _resolverAtrules = ([current, ...tail], result = {}) => { // if (!current) { // return result; // } // const [key, value] = current; // const match = regExp.match(key, /@(.*?)\)/gm, ''); // const matchString = key.replace(match[0], '').replaceAll(' :', ':'); // const stringKey = removeDoubleSpace(matchString.trim()); // if (stringKey.includes('@')) { // return _resolverAtrules([[stringKey, value], ...tail], result); // } else { // const labelHash = getLabelHash(key); // const str = ` ${stringKey} { label: ${labelHash}; ${value} } `; // result[match[0]] = result[match[0]] // ? (result[match[0]] += str) // : (result[match[0]] = str); // return _resolverAtrules(tail, result); // } // }; // ...................................... //// resolver Keyframes // ...................................... const resolverKeyframes = (array) => { return array.reduce((acc, [key, value]) => { if (!value) { return acc; } const propArray = key.split(' '); `${propArray[propArray.length - 2]}`; const rulename = `${propArray[propArray.length - 1]}`; const labelHash = getLabelHash(key); const str = `label: ${labelHash}; ${value} `; acc[`@keyframes ${rulename}`] = acc[`@keyframes ${rulename}`] ? (acc[`@keyframes ${rulename}`] += str) : (acc[`@keyframes ${rulename}`] = str); return acc; }, {}); }; // ...................................... //// resolver Rulename // ...................................... const resolverRulename = (array) => { return array.reduce((acc, [key, value]) => { const labelHash = getLabelHash(key); const k = key.includes('@') ? key.match(/@.*/gm)[0].trim() : key; return { ...acc, [k]: `label: ${labelHash}; ${value}` }; }, {}); }; const resolverAtrules = (atrules) => { return atrules.reduce((acc, [key, value]) => { const labelHash = getLabelHash(value); return { ...acc, [key]: `label: ${labelHash}; ${value}`, }; }, {}); }; // ...................................... //// resolver // ...................................... const resolver = (styles) => { const serialized = styles.reduce((acc, prev) => { const key = prev.name.includes('@') ? prev.name : prev.props.join(' '); const value = prev.value.join(' '); return { ...acc, [key]: value, }; }, {}); //--- rules const rules = Object.entries(serialized).reduce( (acc, [key, value]) => { const prop = regExp.test(key, /@(.*?)\)/gm, '') ? 'atrule' : regExp.test(key, /@keyframes/gm, '') ? 'keyframes' : 'rulename'; acc[prop] = { ...acc[prop], [key]: value }; return acc; }, { rulename: {}, atrule: {}, keyframes: {}, } ); const rulenames = resolverRulename(Object.entries(rules.rulename)); const atrules = resolverAtrules(Object.entries(rules.atrule)); const keyframes = resolverKeyframes(Object.entries(rules.keyframes)); return prettier({ ...rulenames, ...atrules, ...keyframes }); }; // ...................................... //// serializeResolver // ...................................... const serializeResolver = (styles) => arrayOwnProperties(resolver(styles)); // ...................................... //// serialize // ...................................... const serialize = (args, classnameRoot) => { return typeof args === 'string' ? { name: classnameRoot, styles: serializeResolver(serializeString(args, classnameRoot)), } : { name: classnameRoot, styles: serializeResolver(serializeObject(args, classnameRoot)), }; }; const isBrowser$1 = typeof document !== 'undefined'; // ...................................... //// create Style Element // ...................................... const createElement = (cache, name) => { let node = document.createElement('style'); node.setAttribute('data-e-style', `${cache.key} ${name}`); if (cache.nonce !== undefined) { node.setAttribute('nonce', cache.nonce); } document.head.appendChild(node); node.setAttribute('data-s', ''); return node; }; const insertRules = (sheet, cache, name) => { const node = createElement(cache, name); node.appendChild(document.createTextNode(sheet)); }; const styleSheet = (cache, name, styleSheets) => { styleSheets.map((sheet) => insertRules(sheet, cache, name)); }; // ...................................... //// create Style sheet // ...................................... const insertStyleSheet = (cache, styleSheets) => { if (isBrowser$1) { let arr = []; const ssrStyles = document.querySelectorAll( `style[data-e-style^="${cache.key} "]` ); Array.prototype.forEach.call(ssrStyles, (node, i) => { const array = node.innerHTML.split(' '); const _name = array[0] ? array[0].trim() : array[0]; arr.push(`${_name}`); }); if (arr.includes(`.${styleSheets.name}`)) { return; } styleSheet(cache, styleSheets.name, styleSheets.styles); } }; // ...................................... //// Patterns // ...................................... // ...................................... //// remove Comment // ...................................... const replacementInLineComments = (str) => (match, p1, p2, p3) => { const preserveHTTP = str.substring(p2 - 10, p2); return preserveHTTP.includes('http') ? match : ''; }; //--- e.g. // ...COMMENTS const removeInLineComments = (str) => { return str.replaceAll(inLineCommentsPattern, replacementInLineComments(str)); }; //--- e.g. /* COMMENTS */ const removeMultLineComments = (str) => { return str.replaceAll(removeCommentPattern, ''); }; // ...................................... //// remove Comment // ...................................... //--- single-line or multi-line const removeComments = (str) => { /*--- remove all comments */ return removeInLineComments(removeMultLineComments(`__$__${str}__$__`)); }; // ...................................... //// insert Marks // ...................................... const removeMarks = (str, marks) => { return str.replaceAll(marks, ''); }; const breakLine = (str, mark) => { return str .replaceAll('\n', ' \n ') .replaceAll('\\n', ' \\n ') .replaceAll('\n', mark) .replaceAll('\r', mark) .replaceAll('\\n', mark); }; const insertMarks = (args) => { return Array.isArray(args) ? args.map((str) => breakLine(str, '__$__')) : breakLine(args, '__$__'); }; const createValuesMarks = (values) => { return values.flat().map((v) => (typeof v === 'function' ? '__F__' : v)); }; const finalizeRules = (str) => { return removeDoubleSpace(removeMarks(removeComments(str), '__$__')).trim(); }; const interpolations = (args, values) => { const argsMarks = insertMarks(args); const strRaw = String.raw({ raw: argsMarks }, ...values); return finalizeRules(strRaw); }; // ...................................... //// create Rules // ...................................... const createRules = (args, ...values) => { values = Array.isArray(values) ? values.flat() : [values].flat(); if (!args) { return ''; } if (values.flat().length) { if (isObject(args)) { return args; } else { const valuesMarks = createValuesMarks(values); const rules = interpolations(args, valuesMarks); return rules; } } else { const rules = interpolations(args, values); return rules; } }; // ...................................... //// utils // ...................................... const isBrowser = typeof document !== 'undefined'; // ...................................... //// create Css String To Style // ...................................... //--- creates the css string that will be inserted in the style tag const createCssStringToStyle = (array) => { return array.map((m) => { const key = ownkey(m); const classname = key.startsWith('@') ? key : `${key}`; const value = ownValue(m); const str = `${classname} { ${removeLabel(value)} }`; return removeDoubleSpace(str); }); }; // ...................................... //// process Styles // ...................................... const processStyles = (cache, serialized) => { if (isBrowser) { //--- insertStyles insertStyles( cache, { name: serialized.name, styles: serialized.styles, }); //--- createStyleSheet insertStyleSheet(cache, { name: serialized.name, styles: createCssStringToStyle(serialized.styles), }); console.log('[CLIENT]: INSERTED...', { serialized, eCache: cache }); } else { //--- insertStylesServer (cache.inserted is an array on the server) insertStylesServer( cache, { name: serialized.name, styles: serialized.styles, }); console.log(' '); // console.log( // ` [SERVER]: INSERTED...`, // JSON.stringify({ serialized, eCache: cache }, null, 2) // ); } return serialized.name.startsWith('.') ? serialized.name.slice(1) : serialized.name; }; // ...................................... //// process Instance // ...................................... const processInstance = (cache, rules, classnameRoot) => { if (isBrowser) { // //--- debug // // processInstanceDebug(cache, rules, classnameRoot); // // return classnameRoot; //--- Because in cache inserted the key is just the hash const classnameHash = classnameRoot.replace(`${cache.key}-`, ''); //--- client if (cache.inserted && cache.inserted[classnameHash]) { console.log('[CLIENT]: CACHE...', { classnameRoot, eCache: cache }); return classnameRoot; } else { const serialized = serialize(rules, classnameRoot); return processStyles(cache, serialized); } } else { //--- server if (classnameRoot.startsWith('css-')) { return classnameRoot; } const serialized = serialize(rules, classnameRoot); return processStyles(cache, serialized); } }; // ...................................... //// ccsInstance // ...................................... const ccsInstance = (cache, args, values) => { const key = cache.key; const rules = createRules(args, values); const hash = createHash(rules); const classnameRoot = `${key}-${hash}`; return processInstance(cache, rules, classnameRoot); }; // ...................................... //// create Instance ('css') // ...................................... let createInstance = (options) => { let cache = createCacheStyle(options, '_CSS'); let css = (args, ...values) => { const classname = ccsInstance(cache, args, values); return classname; }; return { css, cache }; }; const { css, cache } = createInstance( { key: 'css' }); export { cache, css };