UNPKG

@lipemat/postcss-boilerplate

Version:

Dependencies and scripts for a no config grunt postcss compiler.

141 lines (124 loc) 3.72 kB
import {getPackageConfig} from '@lipemat/js-boilerplate-shared/helpers/package-config.js'; import type {Environment} from './config'; export const SHORT_ALPHABET = 'abcdefghijklmnopqrstuvwxyz'; export const ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const classes: { [ filename: string ]: { [ className: string ]: string } } = {}; let counters = [ -1 ]; /** * Check if short CSS classes are enabled. * * Using a helper function to allow for future enhancements. * * @since 4.6.0 */ export function usingShortCssClasses(): boolean { const short = getPackageConfig().shortCssClasses; if ( 'object' === typeof short ) { return Boolean( short.pcss ); } return Boolean( short ); } /** * Reset all counters. * * @notice Mostly here for unit tests. */ export function resetCounters(): void { counters = [ -1 ]; } /** * Get the next class in the sequence based on: * 1. Single character from SHORT_ALPHABET (prevent conflicts with JS boilerplate). * 2. Incremented character from the `ALPHABET`. * 1. Used once requires 2+ characters. * 2. Grows to 3+ characters as needed. * * @return {string} */ export function getNextClass(): string { const last = counters.length - 1; let totalLetters = ALPHABET.length - 1; // The first level uses the SHORT_ALPHABET. if ( 0 === last ) { totalLetters = SHORT_ALPHABET.length - 1; } if ( counters[ last ] < totalLetters ) { counters[ last ]++; } else { incrementParent(); } return counters.map( ( counter, i ) => { return 0 === i ? SHORT_ALPHABET[ counter ] : ALPHABET[ counter ]; } ).join( '' ); } /** * When we run out of characters on the current level: * 1. Increment the parent level. * 2. Reset the current level and all child levels back to 0. * * If we are out of characters on the parent level or have * no parent level: * 1. Add a new child level. * 2. Reset all levels back to 0. * */ function incrementParent() { let parent = counters.length - 2; let totalLetters = ALPHABET.length - 1; while ( counters[ parent ] !== undefined ) { // The first level uses the SHORT_ALPHABET. if ( 0 === parent ) { totalLetters = SHORT_ALPHABET.length - 1; } if ( counters[ parent ] < totalLetters ) { counters[ parent ]++; // Reset all child levels to 0. while ( counters[ parent + 1 ] !== undefined ) { counters[ parent + 1 ] = 0; parent++; } return; } parent--; } // Add a new level and reset all existing levels. counters.forEach( ( _, i ) => counters[ i ] = 0 ); counters.push( 0 ); } /** * Return a single character unique CSS class name based on * postcss-module's `generateScopedName` callback. * * Tracks CSS classes per each file so duplicate uses of the * same class in a file receive the same result. * * @notice Only enabled if the `package.json` has `shortCssClasses` set to true. * * @link https://github.com/madyankin/postcss-modules#generating-scoped-names */ export const generateScopedName = ( localName: string, resourcePath: string ): string => { classes[ resourcePath ] ||= {}; classes[ resourcePath ][ localName ] ||= getNextClass(); return classes[ resourcePath ][ localName ]; }; /** * Get the hash to generate the CSS module name, or * the `generateScopeName` for short CSS classes * if enabled. * * @note If run into issues with class name conflicts @see b36fc5309 as a more robust alternative. */ export function getGenerateScopeName( env: Environment ) { if ( 'production' === env ) { // Use short CSS classes if enabled. if ( usingShortCssClasses() ) { return generateScopedName; } return '[contenthash:base52:5]'; } return 'Ⓜ[name]__[local]__[contenthash:base52:2]'; }