UNPKG

cssnano

Version:

A modular CSS minifier, built on top of the PostCSS ecosystem.

120 lines (109 loc) • 2.94 kB
import { counter, counterStyle, cssWideKeywords, grid, keyframes, predefinedCounterStyles, } from './slots.js'; const LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; const REMAINDER_CHARACTERS = `${LETTERS}0123456789-_`; // Reserve every word that would make a generated identifier unusable. // existing keywords and predefined counter styles a user style could shadow. const RESERVED_WORDS = new Set([ ...cssWideKeywords, ...keyframes.reservedKeywords, ...counterStyle.reservedKeywords, ...counter.reservedKeywords, ...grid.reservedKeywords, ...predefinedCounterStyles, 'inline', 'list-item', 'page', ]); /** * The leading letter counts in base 52, each * suffix character in base 64, worth one more than its alphabet index. * The arithmetic runs on IEEE-754 doubles, so it is exact only while the * positions stay within Number.MAX_SAFE_INTEGER — around nine quadrillion * renames in one plugin lifetime, orders of magnitude past any stylesheet. * @param {string} word * @return {number | undefined} */ function reservedPosition(word) { const lead = LETTERS.indexOf(word[0]); if (lead < 0) return undefined; let position = lead; let place = 1; for (let i = 1; i < word.length; i++) { const digit = REMAINDER_CHARACTERS.indexOf(word[i]); if (digit < 0) return undefined; position += (digit + 1) * 52 * place; place *= 64; } return position; } /** @type {number[]} */ const RESERVED_POSITIONS = []; for (const word of RESERVED_WORDS) { const position = reservedPosition(word); if (position !== undefined) { RESERVED_POSITIONS.push(position); } } RESERVED_POSITIONS.sort((a, b) => a - b); /** * Count reserved positions at or below a candidate by bisecting the list. * @param {number} position * @return {number} */ function reservedUpTo(position) { let low = 0; let high = RESERVED_POSITIONS.length; while (low < high) { const middle = Math.floor((low + high) / 2); if (RESERVED_POSITIONS[middle] <= position) { low = middle + 1; } else { high = middle; } } return low; } /** * @param {number} num * @return {string} */ function plain(num) { let base = 52; let characters = LETTERS; let character = num % base; let result = characters[character]; let remainder = Math.floor(num / base); if (remainder) { base = 64; characters = `${characters}0123456789-_`; while (remainder) { remainder--; character = remainder % base; remainder = Math.floor(remainder / base); result = result + characters[character]; } } return result; } /** * Map an index to an identifier. * * @param {number} num * @return {string} */ function encode(num) { let position = num; for (;;) { const stepped = num + reservedUpTo(position); if (stepped === position) return plain(position); position = stepped; } } export default encode;