UNPKG

postcss-minify-selectors

Version:

Minify selectors with PostCSS.

59 lines (55 loc) • 2.04 kB
import cssnanoUtils from 'cssnano-utils'; import { needsHexEscapeTerminator } from './hexEscape.js'; const { tokenStart } = cssnanoUtils; /** @typedef {import('./arena.js').SelectorArena} SelectorArena */ /** @typedef {import('./outputOverlay.js').Emit} Emit */ /** @param {SelectorArena} arena @param {number} tokenIndex */ function sourceOffset(arena, tokenIndex) { if (tokenIndex === arena.tokens.length) return arena.source.length; const token = arena.tokens[tokenIndex]; if (!token) { if (tokenIndex === 0) return 0; throw new RangeError('source emission has an invalid token boundary'); } return tokenStart(token); } /** * Serialize trusted normalizer output, where node emissions always mean an * unchanged source-backed subtree. Joining restores hex escape terminators that * text emits omit, judged from the bytes that actually follow (see * `needsHexEscapeTerminator`). * @param {SelectorArena} arena * @param {Emit | undefined} root */ export function serializeNormalized(arena, root) { if (root === undefined) return arena.source; /** @type {Emit[]} */ const work = [root]; /** @type {string[]} */ const output = []; let previous = ''; while (work.length > 0) { const emit = work.pop(); if (!emit) break; let value; if (emit.kind === 'text') value = emit.value; else if (emit.kind === 'source') value = arena.source.slice(emit.start, emit.end); else if (emit.kind === 'node') { const node = arena.nodes[emit.node]; if (!node) throw new RangeError('emission references an unknown arena node'); value = arena.source.slice( sourceOffset(arena, node.startToken), sourceOffset(arena, node.endToken) ); } else { for (let index = emit.items.length - 1; index >= 0; index--) work.push(emit.items[index]); continue; } if (value === '') continue; if (needsHexEscapeTerminator(previous, value)) output.push(' '); output.push(value); previous = value; } return output.join(''); }