UNPKG

cssnano

Version:

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

167 lines (143 loc) • 3.43 kB
import { isTokenIdent, isTokenWhiteSpaceOrComment, tokenize, TokenType, } from '@csstools/css-tokenizer'; import cssnanoUtils from '#utils'; import mappings from './lib/map.js'; const { asciiLowerCase } = cssnanoUtils; const displayPropertyRegex = /^[dD][iI][sS][pP][lL][aA][yY]$/v; const displayOutside = new Set(['block', 'inline', 'run-in']); const displayInside = new Set([ 'flow', 'flow-root', 'table', 'flex', 'grid', 'ruby', ]); /** * @param {string} value * @return {string} */ function toASCIILowerCase(value) { return asciiLowerCase(value); } /** * @param {string} identifier * @param {{ outer?: string, inner?: string, listItem?: string }} state * @return {boolean} */ function addIdentifier(identifier, state) { if (identifier === 'list-item') { if (state.listItem) { return false; } state.listItem = identifier; } else if (displayOutside.has(identifier)) { if (state.outer) { return false; } state.outer = identifier; } else if (displayInside.has(identifier)) { if (state.inner) { return false; } state.inner = identifier; } else { return false; } return true; } /** * @param {string} value * @return {string} */ function transform(value) { let count = 0; /** @type {{ outer?: string, inner?: string, listItem?: string }} */ const state = {}; for (const token of tokenize({ css: value })) { if (token[0] === TokenType.EOF) { break; } if (isTokenWhiteSpaceOrComment(token)) { continue; } if (!isTokenIdent(token)) { return value; } count++; if (count > 3) { return value; } const identifier = toASCIILowerCase(token[4].value); if (!addIdentifier(identifier, state)) { return value; } } if (count < 2) { return value; } if ( state.listItem && state.inner && state.inner !== 'flow' && state.inner !== 'flow-root' ) { return value; } const key = state.listItem ? `${state.outer ?? ''},${state.inner ?? ''},${state.listItem}` : `${state.outer ?? ''},${state.inner ?? ''}`; return mappings.get(key) ?? value; } /** * @param {import('postcss').Declaration} decl * @param {string} value */ function assignValue(decl, value) { decl.value = value; if (decl.raws.value?.raw) { decl.raws.value = { raw: value, value }; } } /** * @return {import('postcss').Plugin} */ function pluginCreator() { return { postcssPlugin: 'postcss-normalize-display-values', prepare() { const cache = new Map(); return { /** * @param {import('postcss').Root} css */ OnceExit(css) { css.walkDecls(displayPropertyRegex, (decl) => { const value = decl.raws.value?.value === decl.value ? (decl.raws.value.raw ?? decl.value) : decl.value; if (!value) { return; } if (cache.has(value)) { assignValue(decl, cache.get(value)); return; } const result = transform(value); assignValue(decl, result); cache.set(value, result); }); }, }; }, }; } /** @type {true} */ pluginCreator.postcss = true; const moduleExports = pluginCreator; export { moduleExports as default, moduleExports as 'module.exports' };