main.ml 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. open Lexing
  2. open Types
  3. type args = {
  4. infiles : string list;
  5. outfile : string option;
  6. verbose : bool;
  7. whitespace : bool;
  8. simple : bool;
  9. shorthands : bool;
  10. duplicates : bool;
  11. sort : bool;
  12. }
  13. let usage =
  14. "Usage: " ^ Sys.argv.(0) ^
  15. " [<options>] [<file> ...]\n\
  16. \n\
  17. Generic options:\n \
  18. -h, --help Show this help message\n \
  19. -v, --verbose Verbose mode: show compression rate\n \
  20. -o <file>\n \
  21. --output=<file> Output file (defaults to stdout)\n \
  22. <file> ... Input files (defaults to stdin or \"-\")\n\
  23. \n\
  24. Optimization flags (default is -w -c -s -d):\n \
  25. -w, --whitespace Eliminate unnecessary whitespaces (has the greatest \
  26. effect, omit for pretty-printing)\n \
  27. -c, --simple Shorten colors, font weights and nth-child\n \
  28. -s, --shorthands Generate shorthand properties\n \
  29. -d, --duplicates Prune duplicate properties (WARNING: may affect \
  30. cross-browser hacks)\n \
  31. -p, --pretty Shorthand for -c -s -d\n \
  32. \n\
  33. Formatting options:\n \
  34. -r, --sort Sort declarations in each ruleset (always on when \
  35. --shorthands is enabled)\n \
  36. -e, --echo Just parse and pretty-print, no optimizations\n\
  37. "
  38. let parse_args () =
  39. let infiles = ref [] in
  40. let outfile = ref None in
  41. let verbose = ref false in
  42. let whitespace = ref false in
  43. let simple = ref false in
  44. let shorthands = ref false in
  45. let duplicates = ref false in
  46. let sort = ref false in
  47. let echo = ref false in
  48. let show_usage () = prerr_string usage; raise Exit_success in
  49. let set_pretty () = simple := true; shorthands := true; duplicates := true in
  50. let add_infile = function
  51. | "-" -> infiles := []
  52. | filename -> infiles := filename :: !infiles
  53. in
  54. let specs = [
  55. ('h', "help", Some show_usage, None);
  56. ('v', "verbose", Getopt.set verbose true, None);
  57. ('o', "output", None, Some (fun file -> outfile := Some file));
  58. ('w', "whitespace", Getopt.set whitespace true, None);
  59. ('c', "simple", Getopt.set simple true, None);
  60. ('s', "shorthands", Getopt.set shorthands true, None);
  61. ('d', "duplicates", Getopt.set duplicates true, None);
  62. ('p', "pretty", Some set_pretty, None);
  63. ('r', "sort", Getopt.set sort true, None);
  64. ('e', "echo", Getopt.set echo true, None);
  65. ] in
  66. Getopt.parse_cmdline specs add_infile;
  67. match {
  68. infiles = List.rev !infiles;
  69. outfile = !outfile;
  70. verbose = !verbose;
  71. whitespace = !whitespace;
  72. simple = !simple;
  73. shorthands = !shorthands;
  74. duplicates = !duplicates;
  75. sort = !sort;
  76. } with
  77. (* disable optimizations when --echo is specified *)
  78. | args when !echo = true ->
  79. { args with
  80. whitespace = false;
  81. simple = false;
  82. shorthands = false;
  83. duplicates = false }
  84. (* enable all optimizations by default *)
  85. | { whitespace = false;
  86. simple = false;
  87. shorthands = false;
  88. duplicates = false;
  89. _ } as args ->
  90. { args with
  91. whitespace = true;
  92. simple = true;
  93. shorthands = true;
  94. duplicates = true }
  95. | args -> args
  96. let parse_files = function
  97. | [] ->
  98. let input = Util.input_buffered stdin 512 in
  99. (input, Parse.parse_input "<stdin>" input)
  100. | files ->
  101. let rec loop = function
  102. | [] -> []
  103. | filename :: tl ->
  104. if not (Sys.file_exists filename) then
  105. raise (Failure ("file " ^ filename ^ " does not exist"));
  106. let input = Util.input_all (open_in filename) in
  107. let stylesheet = Parse.parse_input filename input in
  108. (input, stylesheet) :: loop tl
  109. in
  110. let inputs, stylesheets = List.split (loop files) in
  111. (String.concat "" inputs, List.concat stylesheets)
  112. let handle_args args =
  113. let write_output =
  114. match args.outfile with
  115. | None -> print_endline
  116. | Some name ->
  117. fun css -> let f = open_out name in output_string f css; close_out f
  118. in
  119. let switch flag fn = if flag then fn else fun x -> x in
  120. let input, css = parse_files args.infiles in
  121. let css = css
  122. (* unfold before pruning duplicates so that shorthand components are
  123. * correctly pruned *)
  124. |> switch args.shorthands Shorthand.unfold_stylesheet
  125. |> switch args.duplicates Duplicates.compress
  126. |> switch args.shorthands Shorthand.compress
  127. |> switch args.simple Simple.compress
  128. |> switch args.sort Util.sort_stylesheet
  129. in
  130. let output =
  131. if args.whitespace
  132. then Stringify.minify_stylesheet css
  133. else Stringify.string_of_stylesheet css
  134. in
  135. write_output output;
  136. if args.verbose then begin
  137. let il = String.length input in
  138. let ol = String.length output in
  139. Printf.fprintf stderr "compression: %d -> %d bytes (%d%% of original)\n"
  140. il ol (int_of_float (float_of_int ol /. float_of_int il *. 100.))
  141. end
  142. (* Main function, returns exit status *)
  143. let main () =
  144. begin
  145. try
  146. handle_args (parse_args ());
  147. exit 0
  148. with
  149. | Loc_error (loc, msg) ->
  150. Util.prerr_loc_msg loc ("Error: " ^ msg);
  151. | Box_error (box, msg) ->
  152. prerr_endline ("Error: " ^ msg ^ ": " ^ Stringify.string_of_box box);
  153. | Getopt.Error msg ->
  154. prerr_endline ("Error: " ^ msg ^ "\n");
  155. prerr_string usage;
  156. | Failure msg ->
  157. prerr_endline ("Error: " ^ msg);
  158. | Exit_success ->
  159. exit 0
  160. end;
  161. exit 1
  162. let _ = main ()