main.ml 5.0 KB

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