Revision history for CSS::Minifier
0.0.13 2026-06-14T05:52:39+03:00
- Fix: $UNSAFE-RE now guards all CSS Values Level 4 math functions
(abs, sign, round, mod, rem, pow, sqrt, hypot, sin, cos, tan,
atan, atan2, asin, acos, exp, log) — prevents incorrect
zero-unit stripping and color normalization inside these
functions
- Fix: !consolidate-shorthands dropped longhands with
non-lowercase names; !write-at-rule omitted ; before
trailing-declarations; unclosed /*! at EOF leaked into output;
CSS_MINIFIER_CACHE_MAX=0 caused div by zero; gap-text heuristic
now rejects tokens containing (
- Fix: !url-quote strips outer quotes and escapes inner matching
quotes; $td.normalized = Nil instead of Str (Merging)
- Fix: LRU hit-path promotion now fires during fill;
declarations-to-key no longer caches .normalized as side effect;
!url-quote-value avoids re-combing the same string via
skip-quoted(@chars, ...)
- Perf: hot loops use pre-comb'd @chars[\$j]; parse-body uses
\$body.substr(\$i, \$end-\$i) instead of slice-then-join;
find-braceless-end skips past */ closing slash directly
- Quality: consistent indentation in Minifier.rakumod;
find-braceless-end uses skip-comment-chars; ring-buffer
arithmetic documented; dead default guards annotated
0.0.12 2026-06-14T02:58:17+03:00
- Fix: CLI — -l 1 (separate arg) now works; root cause was Raku
regex [12] being a group matching "12", not a character class;
also refactored given/when → if/elsif to avoid implicit-break
interaction with next
- Fix: strip-zero-units now safe inside quoted strings; handles
.0px/.0em (leading-digit-less decimals) and respects url() with
arbitrary paren nesting; $1. alpha in rgb()/hsl() matches
trailing dot with no zeros
- Fix: backslash-escaped braces no longer miscounted as block
delimiters; unclosed /*! at EOF preserved in output; } at depth
0 before any { no longer crashes via Nil brace in substr
- Security: $SENTINEL broadened to \x[E000]\x[E001] to virtually
eliminate PUA collision risk
- Quality: !split-gap-buffer skips tokens containing quotes;
default {die} guards on Statement dispatch; license extraction
returns Pair (no $doc mutation); Declaration.normalized default
Str → Nil
- Perf: O(1) position-tracked LRU in comb-cached (every hit
promotes); .comb.List moved outside lock; O(n²) → O(n) in
parse-body/ strip-braceless-comments; $h %= 360; pass @sel-chars
to skip-quoted
0.0.11 2026-06-13T05:08:48+03:00
- Fix: parser — declarations between nested at-rule blocks not
dropped; gap text without `;` correctly split; braceless at-rule
comment before `;` handled; !find-block-range skips `/* */`
comments; case-insensitive property sort in declarations-to-key;
EOS zero-unit `>` guard
- Fix: normalization — digit-only hex shortened; :color-names
font-weight/ zero-units preserved; redundant !apply-font-weight
removed; decimal rgb rounding; !color-masks :short flag
respected; symmetric custom-prop merge barrier; indent 4-spaces
in !merge-decls
- Fix: dedup/merging — trailing-declarations barriers for both
Dedup and Merging; redundant key extraction; case-insensitive
shorthand/merge lookup; custom property case preservation
- Fix: CLI — -o without argument gives clear "Missing file path"
-error; o without value; !extra-plugins at level 1
- Perf: O(1) ring-buffer comb-cache eviction; restore comb-cached
for long selectors; cache @body-chars via comb-cached in
!parse-at-rule; clear-comb-cache per minify(); LRU
direct-return; remove 148-entry named-color guard; reuse
!find-block-range for trailing-decl scan
- Quality: %DISPATCH anon-sub → direct given dispatch; dead
:track-last-close & $close & @.braceless removed;
has-top-level-comma → split-top-level .elems check;
extract-braceless-at helper; guard-color inlined; C-style loop →
.reverse; .elems > 0 checks; $no-color-ops → $full-normalize;
--> Str() for Nil returns; COMB-CACHE-SMALL env override; bare
`|` resets $last-comb; !extract-braceless simplified;
combine-adjacent-stmts; strip-braceless-comments;
with-url-quarantine/normalize-value subs; braceless as AtRule
objects
- Doc: POD for clear-comb-cache; CSS native nesting unsupported
note
- Test: baseline 100%; 9+ new assertions
0.0.10 2026-06-12T06:05:38+03:00
- Fix: color/value normalization — balanced-paren URL quarantine;
hsl→hex; hsl/hsla rejects 1% alpha; rgb-values-to-hex bare 100
alpha & leading-dot numbers; font-weight exact match; case-fold
dedup (Normalizer, Writer); UNSAFE-RE guard; embedded-unit
guard; 8-digit hex anchor; rgb-values clamp; remove bogus
post\-rule\; dead-code cleanup
- Fix: parser — quote-aware comment stripping for /* inside
url("..."); @layer; semicolon-terminated at-rules (braceless
flag); depth-underflow guards; ] combinator $last-comb reset;
stray * before braceless @-rule; match-variable scoping
- Fix: dedup/merging — selector sort, trailing-declarations
at-rule key, combinator dedup reset on ), !important cascade,
empty-ruleset barrier, Dedup comment broadened beyond @font-face
- Perf: regex→junction (~20% faster parse); O(1) comb-cache
eviction; substr window; named-color pre-check (~10× pipeline);
decl-key cache
- Code quality: dead-code removal, %NAMED/HEX-TO-NAME expansion,
%COLOR-PROPS shared, CLI --level validation; misc edge-cases;
review-5 subtest
- Fix: case-insensitive color/font-weight guards (Writer,
Document); case-insensitive !merge-decls lookup; custom property
case preserved in Normalizer dedup and Writer output
- Test: 5 new test files (at-rule-nesting, unicode, selectors,
supports); 111+ new assertions across 7 files
0.0.9 2026-06-06T05:43:28+03:00
- Refactor: consolidate color/unit helpers in Util, method cleanup
(!same-selectors→eqv, !make-declaration, inline !decl-key, etc.)
- Fix: parser edge cases (has-brace off-by-one, bare-/ double-inc,
etc.)
- Fix: normalization (hex folding, !normalize-quotes \" escape,
rgb float %, empty background guard, non-color props removed
from %COLOR-PROPS)
- Fix: dedup/merge (return instead of mutate, bold/normal word
boundaries, selector identity over .fc, revert-layer, AtRule
barrier check)
- Fix: rgb() rounding (50%→#80), selector case-sensitivity, CLI
--level
- Perf: fast-path guards, grep+map combine, comb-cache gradual
eviction, skip color-masks when color-names enabled
- Code quality: custom-property skip in declarations-to-key,
CSS_MINIFIER_CACHE_MAX env var, !assemble-background-multi
manual max-loop
- Test: delete 01-basic (merged into golden), qqx→run, new
coverage
0.0.8 2026-06-05T07:46:49+03:00
- ~15× perf: comb-cache with binding, skip-quoted copy
elimination, .chars caching in hot loops
- Color masking fixes: url() quarantine, rgb→named, hex→name
word-boundary, Level 4 space-sep rgb, rgba 1.0, %COLOR-PROPS
fast-path
- Parser fixes: OOB in !extract-braceless, find-semicolon $i++,
bare / fallthrough, depth-tracked @import
- Writer fixes: .0 edge case, null-byte sentinel, url-quote
backtrack, UNITS constant, url-quote-value helper
- Plugin fixes: Dedup added to pipeline, extra-plugins die guard,
at-rule-decl-key case sensitivity, dead Values plugin removed
- Dedup/Merging: normalize colors (named→hex, bold→700) and
rgb→hex in declarations-to-key, Unicode .fc in selector dedup
- Refactor: declarations-to-key helper, %DISPATCH table, helper
extraction, dead return cleanup
- Other: CLI --level, @@name, modern CSS units
(cap,ic,lh,rlh,vb,vi,cq*), comb-cache 500-cap + Lock thread
safety
0.0.7 2026-06-04T03:35:20+03:00
- AST rewrite: own Parser/Document/Normalizer replaces
CSS::Stylesheet/ CSS::Writer/CSS::Properties — zero deps
(META6.json []), drop :$preserve-unknown
- Add !dedup-declarations within a ruleset
- Add !consolidate-shorthands (margin, padding, border, outline,
list-style, background, font); skip var()/calc() etc.; fix
duplicate-declaration, ordering, mixed-!important bugs
- Fix !write-at-rule space check, !extract-braceless quote
walkers, Writer quote norm/hex uppercasing
- Add Dedup !important consistency, background/font consolidation
(multi-layer with cycling, size/origin/clip; font with stretch/
line-height), %CUSTOM dispatch table, assembly helpers, comma
helpers in Util, $CSS-WIDE-RE guard
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
- Fix @view-transitions/declaration-only at-rules dropped;
@font-face descriptors dropped by !walk-decls
- Fix !merge-props !important cascade; :has()/:is()/:where() dupe
rulesets
- Fix bogus @media not all detection, Nth-block targeting in
!find-at-rule-block/!prop-exists, quote normalisation, regex
injection
- Rewrite tests: exact golden output, add 03-edge-cases/04-golden
- Fix merge-qualified/remove-selector/walk-decls/dedup-selector
bugs
- Fix xpath crash on ::pseudo-elements (monkey-patch) and
idempotency
- Fix non-adjacent merging with propertyless rules, cascade-order
during same-decl merge, @keyframes selector order (@order array)
- Rewrite !walk-decls with parse-body character-walker (handles
strings, parens, comments)
- Rewrite /*! */ license extraction with character-walking state
machine
- Add !minify-value (color, zero-unit, font-weight, quote
normalization) to re-injection path; add !normalize-all-decls
post-processing pass
- Fix hex shortening regex, zero-unit negative-lookbehind,
parse-body double-increment, quote-aware walkers
- Fix re-minification growth: normalize selectors/preludes to
match pipeline output format
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