CSS compressor written in OCaml

Taddeus Kroes 1be0e4026c Update cli usage in README 10 years ago
test f7c7c13cba Add support for :nth-child formula syntax 10 years ago
.gitignore cccf4c432e Added generated file to ignore file 11 years ago
Makefile 83d43b9619 Switch to Getopt for parsing cli args 10 years ago
README.md 1be0e4026c Update cli usage in README 10 years ago
color_names.ml bf099c1049 Bugfix in color conversion 11 years ago
duplicates.ml 591c9af716 Code cleanup 11 years ago
lexer.mll b54ca4478e Cleanup 10 years ago
main.ml 83d43b9619 Switch to Getopt for parsing cli args 10 years ago
parse.ml 70f032a31d Lexer now correctly tracks line numbers + some general cleanup 11 years ago
parser.mly 36988140ac Solved shift/reduce 10 years ago
selector.ml b54ca4478e Cleanup 10 years ago
shorthand.ml abb3154faa Prevent crash on malformed background shorthand 10 years ago
simple.ml f7c7c13cba Add support for :nth-child formula syntax 10 years ago
stringify.ml f7c7c13cba Add support for :nth-child formula syntax 10 years ago
types.ml 4350368cd7 Add todo item + moved comment 10 years ago
util.ml d262781999 border:none now also becomes border:0 after shorthand generation 10 years ago

README.md

About

mincss is an extendible CSS minifier written in OCaml. It features a complete parser for the CSS3 language, along with type definitions that are consistent with the official CSS specification and a traversal utility function for use in transformation passes.

Installation

For now, there is no easy installation option for mincss (yet). An up-to-date 64-bit ELF binary is available for download here. You can also build the binary from source (see Building mincss below).

Features

Whitespace compression

a,                                  |  a,.myclass [class~="foo"]>p{color:#fff}
.myclass [class ~= "foo"] >  p {    |
    color: #fff;                    |
}                                   |

Compression of simple expressions

color: rgb(257, -2, 0);             |  color: red;
color: rgb(67%, 67.5%, 68%);        |  color: #abacad;
color: #aaffbb;                     |  color: #afb;
color: white;                       |  color: #fff;
font-weight: normal;                |  font-weight: 400;

Generation of shorthand properties

font-weight: normal;                |  font: normal 12px/15px sans-serif;
font-size: 12px;                    |
line-height: 15px;                  |
font-family: sans-serif;            |

Any existing shorthands are first unfolded into their non-shorthand counterparts to be folded back later. This means that if --duplicates is enabled, the last value is used for shorthand generation, merging separate :

font: normal 12px/15px sans-serif;  |  font: bold 12px/15px sans-serif;
font-weight: bold;                  |

Pruning duplicate declarations

color: #000;                        |  color: #fff;
color: #fff;                        |

Note that !important annotations are correctly handled:

color: #eee !important;             |  color: #000 !important;
color: #000 !important;             |
color: #fff;                        |

Sorting declarations

The --sort command-line option sorts declarations alphabetically. This option is disabled by default since it does not affect file size.

Command-line interface

Output of mincss -h:

Usage: mincss [<options>] [<file> ...]

Generic options:
-h, --help        Show this help message
-v, --verbose     Verbose mode: show compression rate
-o <file>
--output=<file>   Output file (defaults to stdout)
<file> ...        Input files (defaults to stdin or "-")

Optimization flags (default is -w -c -s -d):
-w, --whitespace  Eliminate unnecessary whitespaces (has the greatest effect, omit for pretty-printing)
-c, --simple      Shorten colors, font weights and nth-child
-s, --shorthands  Generate shorthand properties
-d, --duplicates  Prune duplicate properties (WARNING: may affect cross-browser hacks)
-p, --pretty      Shorthand for -c -s -d

Formatting options:
-r, --sort        Sort declarations in each ruleset (always on when --shorthands is enabled)
-e, --echo        Just parse and pretty-print, no optimizations

Building mincss

Dependencies are OCaml 4.02.0, menhir and Getopt.

Bootstrapping on a Debian system can be done as follows:

$ sudo apt-get install ocaml menhir libgetopt-ocaml-dev git
$ git clone git@github.com:taddeus/mincss.git
$ cd mincss
$ make
$ ./mincss --help

I prefer to use Opam myself because it offers more flexibility:

$ sudo apt-get install opam
$ opam init
$ eval `opam config env`
$ opam switch 4.02.0
$ opam update
$ opam install menhir getopt

TODO / bugs

  • border shorthand generation produces out-of-order results when direction-specific declarations follow a generic border declaration. This produces inequivalent CSS, and could be fixed by unfolding each generic border declaration into four direction-specific ones, and sybsequently generating the shortest possible representation of the resulting box model. Edit: a better/easier solution is to generate shorthand properties at the index of the first merged declaration. This might not work in all cases but it is better than putting everything at the end.
  • border:none could be border:0, or in general any shorthand that has both a style and width property should be transformed from none into 0.
  • Automated test suite: auto-diff css files and expected minified versions.
  • mincss -s test/duplicates.css gets stuck in an infinite loop.