Revision history for CSS::Minifier

0.0.6  2026-06-03T21:12:55+03:00
    - Fix re-minification growth (final): colon in media-feature regex
      `/\(.*:.*\)/` was being interpreted as a Raku regex adverb (`:`)
      instead of a literal colon, so the denormalize-prelude fallback
      in !find-at-rule-block was never triggered.  This caused
      !prop-exists to always return False for any @media qualifier
      whose prelude had been space-normalized by !walk-decls, because
      the exact prelude search (with space after `:`) never matched
      the pipeline output (without space after `:`).  Consequence:
      every property inside @media was re-injected on every pass,
      causing unbounded growth.
    - Add normalize-prelude / denormalize-prelude subroutines to
      bridge the two prelude formats
    - Normalize preludes in !walk-decls so qualifier keys are
      consistent
    - Fix !find-at-rule-block to try both normalized and denormalized
      forms during block search
    - Fix !prop-exists to scan ALL at-rule blocks with the same
      prelude (not just the first) for the inner selector
    - Fix !merge-qualified to find the at-rule block that actually
      contains the target inner selector; add idempotency guard to
      skip re-injection when the exact ruleset already exists in the
      block
    - Fix normalize/denormalize-prelude nested-parens handling
      (e.g. calc(), min(), max()) — use a token with &val to match
      balanced inner parens instead of a bare <-[):]>+ capture

0.0.5  2026-06-03T06:24:07+03:00
    - Fix !normalize-block trailing semicolon: always omit (rebuilds
      from scratch), making output consistent with pipeline blocks
    - Fix !find-qual-end depth counting: O(n) single-pass with
      quote/comment skipping instead of O(n²) rescan from 0
    - Fix !normalize-selector quote walking: use skip-quoted for
      backslash-escape handling
    - Fix split-selectors quote walking: use skip-quoted for
      backslash-escape handling (same bug, Util.rakumod)
    - Update Merging.rakumod POD: propertyless rules are skipped
      (not barriers)
    - Update README Level 2: add propertyless-rules note
    - Add test coverage: escaped-quote attribute selector, content
      brace depth guard, @keyframes margin:0px, no-trailing-newline
    - Fix !normalize-all-decls inline quote walkers: use skip-quoted
      (fixes double-backslash \" edge case)
    - Fix split-xpath quote walking: use skip-quoted for consistency
      (Util.rakumod)

0.0.4  2026-06-03T04:48:31+03:00
    - Fix order stability: insert re-injected rulesets at their
      natural @order position (after predecessor) instead of appending
      at end, so first-pass and second-pass selector order is
      identical
    - Add rgb()/rgba() → hex shortening to !minify-value
      (post-processing pass), closing the gap where rgb() values
      inside @keyframes and other pipeline-bypassed blocks stayed
      unshortened
    - Remove stale Merging-plugin !important warning from README —
      !important is part of the Str(:optimize) grouping key, so mixed
      groups never form
    - Fix re-minification growth (remaining): normalize combinator
      spacing (+ / > / ~) in !normalize-selector so selectors like
      .foo+.bar (no space before +) match the pipeline output .foo +
      .bar (with space).  Always call !normalize-selector (not just
      for comma-containing selectors) to catch this case. Prevents
      runaway re-injection on subsequent passes for any selector
      containing tight combinators.

0.0.3  2026-06-03T03:39:52+03:00
    - Fix braceless at-rules (@import, @charset, @namespace) silently
      dropped — extract before parse, prepend to output
    - Fix braceless at-rules trailing newline — suppress separator
      when no ruleset follows
    - Fix @view-transitions and other declaration-only at-rules
      dropped by pipeline
    - Fix @font-face descriptors dropped by !walk-decls
      (e.g. font-display)
    - Fix !merge-props to respect !important cascade
    - Fix :has()/:is()/:where() duplicate rulesets — strip spurious
      truncated prefix ruleset
    - Fix prefers-color-scheme/contrast bogus @media not all — replace
      with original query
    - Fix !replace-bogus-not-all genuine-guard (space before { in
      %by-sel key was missing)
    - Fix !find-at-rule-block and !prop-exists to target correct Nth
      block for duplicate at-rules (e.g. multiple @font-face); fall
      back to block 0 when Nth block was deduped away
    - Fix !find-at-rule-block advance past brace instead of past
      prelude (fragile with space-before-{ variant)
    - Fix !merge-qualified double-semicolon when block body already
      ends with semicolon
    - Fix !prop-exists to scan ALL rulesets for a selector (not just
      the first one), preventing spurious re-injection when
      duplicate-selector rulesets exist at level 1
    - Fix !prop-exists quote normalisation (pre-scanned selectors use
      `"`, pipeline output uses `'`)
    - Fix !prop-exists boundary guard — skip substring matches
      (e.g. `#header` inside `[data-bs-theme='dark'] #header`) while
      allowing valid merged-group matches (e.g. `h1` after `, `)
    - Rewrite 01-basic and 02-plugins with exact golden output (was
      loose regex/count checks)
    - Add 03-edge-cases — 11 subtests covering empty input,
      !important, custom properties, braceless at-rules, attribute
      selectors, nested at-rules, chained merging, @font-face, and more
    - Add 04-golden — 57 exact-output regression tests covering the
      full minification surface including @keyframes, combinators,
      rgb(), @view-transitions, :has()/:is()/:where(),
      prefers-color-scheme, and multi-block @font-face
    - Fix !has-selector-in regex injection — wrap $sel in quotemeta to
      escape metacharacters
    - Fix !merge-qualified inner quote mismatch — normalise `"` → `'`
      for at-rule inner selectors
    - Fix !replace-bogus-not-all inner quote mismatch — same
      normalisation for the media-query path
    - Fix !remove-selector boundary guard — walk backwards past \s
      chars (not just `}`) to handle merged output with :sep("\n")
      separators
    - Fix !walk-decls NUL-counter grep — use explicit block ({ ... })
      instead of broken WhateverCode || to count existing NUL-suffixed
      keys for 3+ duplicate at-rule blocks (e.g. three @font-face)
    - Fix !dedup-selector-lists split on ', ' breaking with attribute
      selectors containing commas — add bracket/paren/string-aware
      split-xpath sub to Util; dump naive split(', ') in Merging; use
      .splice instead of = for AST mutation
    - Fix !merge-decls spurious re-injection after pipeline dedup —
      dedup selector lists in qualifier before prop-exists check via
      new split-selectors sub; re-inject using deduped qualifier
    - Fix upstream xpath crash on pseudo-elements (::before, ::after,
      etc.) — monkey-patch missing `xpath-pseudo-elem` method onto
      CSS::Selector::To::XPath
    - Fix idempotency — strip CSS comments before declaration
      extraction in !walk-decls so re-minified output doesn't grow
    - Fix non-adjacent same-declaration merging — propertyless rules
      are now treated as barriers (last), preserving cascade order
    - Fix propertyless rules break non-adjacent merging — change from
      `last` to `$i++; next` so parser-artifact propertyless rulesets
      are skipped instead of acting as hard barriers
    - Fix cascade-order change during same-declaration merging — track
      last-merged position for each group and sort by it before
      assigning to $stylesheet.rules, preserving which rule wins by
      cascade order
    - Fix @keyframes keyframe selector order — add @order array
      alongside %by-sel in !walk-decls, iterate @order in !merge-decls
      Phase 2 instead of %by-sel.kv (Raku Hash iteration is
      randomized)
    - Remove dead `@skipped` / `@merged.append` code from Merging
      plugin
    - Update documentation: README and POD limitations reflect fixed
      bugs
    - Fix !walk-decls regex fragility — replace <-[:;]>+ / <-[;}]>+
      regex with parse-body character-walker that tracks strings,
      paren depth, and comments, correctly handling ; and } inside
      values
    - Fix /*! */ license extraction inside string values — replace
      naive regex with !extract-licenses character-walking state
      machine
    - Normalize declaration values in !merge-decls re-injection path —
      add !minify-value method that shortens colors, removes
      zero-units, normalizes font-weight, and normalizes quotes (" →
      ') for re-injected declarations
    - Add !normalize-all-decls post-processing pass — walks the output
      string and normalizes values + deduplicates declarations in ALL
      {…} blocks, catching @keyframes keyframes, nested at-rules, and
      other blocks the pipeline can't reach (recursive leaf-block
      walker)
    - Fix broken hex shortening regex in !minify-value (was using
      self- referential captures that never matched; now properly
      backrefs each hex digit pair) so 6-char colors are shortened in
      @keyframes keyframe blocks and other post-processed blocks
    - Fix zero-unit regex in !minify-value — added `<!after \d>`
      negative lookbehind to prevent stripping units from non-zero
      values containing a `0` digit (e.g. `30px` → `30`); now only
      standalone `0px` → `0`
    - Fix golden test 70 for @keyframes normalization — accept both
      keyframe selector orders (from/to vs to/from) since hash
      iteration order in %by-sel is non-deterministic between Raku
      runs
    - Fix parse-body double-increment when : or ; found inside parens
      (skipped one character after :/; inside functions — never fires
      in practice since : in property names is invalid CSS)
    - Fix has-brace check in !walk-decls — use quote-aware walker
      instead of naive / '{' / regex that would false-positive on {
      inside quoted strings (theoretical, extremely rare)
    - Fix braceless at-rule extraction — replace .*?; regex with
      quote/paren-aware find-semicolon walker so ; inside url() or
      strings is correctly skipped (theoretical, ; inside URL is rare)
    - Fix find-semicolon double-increment on (/) — the when blocks
      were incrementing $i inside AND falling through to $i++ at the
      while-loop bottom, skipping one character after each paren
    - Remove dead code (= Empty) in Merging Plugin line 98 — $next is
      about to be dropped, the assignment had no effect
    - Fix re-minification growth — normalize selectors (commas → `, `)
      in !walk-decls and media-query preludes (colons → `: `) in
      !find-at-rule-block so extracted qualifiers match pipeline
      output format, preventing runaway re-injection on subsequent
      passes

0.0.2  2026-05-21T16:27:43+03:00
    - Fix `1em` → `em` regression from CSS::Writer (override write-num
      for `em`/`ex` in Writer.rakumod)
    - Remove %KNOWN-PROPS filter in !walk-decls so all known
      properties are pre-scanned (catches CSS3 values like `position:
      sticky`, `overflow: clip` that the grammar drops)
    - Fix falsy-zero bug in !prop-exists (Raku `0` is falsy, causing
      all rulesets at position 0 to always re-inject)
    - Add %SHORTHAND map in !prop-exists to avoid re-injecting
      longhands consolidated to shorthands by CSS::Properties
      (e.g. `background-color` → `background`)
    - Fix same falsy-zero bug in at-rule path of !prop-exists

0.0.1  2026-05-21T01:30:31+03:00
    - Initial version
