UNPKG

@merkur/plugin-css-scrambler

Version:

Merkur plugin for scrambling CSS classes.

95 lines (88 loc) 2.97 kB
'use strict'; var core = require('@merkur/core'); var classnames = require('classnames'); const CLASSNAME_CHARS = ('abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '-').split(''); const EXTENDED_CLASSNAME_CHARS = (CLASSNAME_CHARS.join('') + '0123456789').split(''); function numberToCssClass(number) { if (number < CLASSNAME_CHARS.length) { return CLASSNAME_CHARS[number]; } // we have to "shift" the number to adjust for the gap between base53 and // base64 encoding number += EXTENDED_CLASSNAME_CHARS.length - CLASSNAME_CHARS.length; let className = ''; while (number >= CLASSNAME_CHARS.length) { className = EXTENDED_CLASSNAME_CHARS[number % EXTENDED_CLASSNAME_CHARS.length] + className; number = Math.floor(number / EXTENDED_CLASSNAME_CHARS.length); } if (number) { className = CLASSNAME_CHARS[number - 1] + className; } else { className = '_' + className; } return className; } /** * @param {Array<Array<string>, Array<string>>=} hashingTable * @return {function(...?(string|Object<string, boolean>))} */ function scramblerFactory(hashingTable) { let uniqueHash, prefixes, mainParts; const prefixTable = hashingTable && new Map(); const mainPartTable = hashingTable && new Map(); if (hashingTable) { [uniqueHash, prefixes, mainParts] = hashingTable; for (let i = 0; i < prefixes.length; i++) { prefixTable.set(prefixes[i], numberToCssClass(i)); } for (let i = 0; i < mainParts.length; i++) { mainPartTable.set(mainParts[i], numberToCssClass(i)); } } return (widget, ...args) => { const classNamesSource = classnames(...args); if (!hashingTable) { return { className: classNamesSource }; } const processedClasses = classNamesSource.split(/\s+/).map(className => { const parts = className.split('-'); const prefix = parts[0]; const mainPart = parts.slice(1).join('-'); if (!prefixTable.has(prefix) || !mainPartTable.has(mainPart)) { return className; } return `${prefixTable.get(prefix)}_${mainPartTable.get(mainPart)}_${uniqueHash}`; }).join(' '); return { className: processedClasses, 'data-class': classNamesSource }; }; } function cssScramblePlugin() { return { async setup(widget, widgetDefinition) { const { classnameHashtable } = widgetDefinition; widget.$in.classNameScrambler = scramblerFactory(classnameHashtable); widget.cn = (...args) => widget.$in.classNameScrambler(...args).className; return widget; }, async create(widget) { core.hookMethod(widget, 'info', infoHook); return widget; } }; } async function infoHook(widget, originalInfo, ...rest) { const result = await originalInfo(...rest); return { classnameHashtable: widget.classnameHashtable, ...result }; } exports.cssScramblePlugin = cssScramblePlugin; exports.numberToCssClass = numberToCssClass;