#!/usr/bin/perl # NOTE: this thing needs documented, badly :-) # and cleaned up. Rolling it out as is cause we're in a hurry. # The code, however, does the job (the lib is the important part anyway). use strict; use JavaScript::Squish; use Getopt::Long; use File::Basename(); my $DEBUG = 0; &Main(); sub Main { my $opts = &load_options(); die "Unable to read file [$$opts{src}]\n".&usage() unless -r $$opts{'src'}; my $outfile = $$opts{'dest'}; if ($outfile) { if (-e $outfile && $$opts{'force'}) { print STDERR "WARNING: output file [$outfile] exists. Overwritting in 2 seconds.\n"; sleep 2; } elsif (-e $outfile) { die "Output file [$outfile] already exists.\nSpecify --force or remove the file prior to running.\n"; } open(OUT, "> $outfile") or die "Unable to open output file [$outfile]\n"; } else { *OUT = *STDOUT; } print STDERR "Reading in file...\n" if $DEBUG; die "Unable to read file [$$opts{src}]\n".&usage() unless open(IN,"< $$opts{'src'}"); # slurp in the input my $data; { local($/); $data = ; } close IN; my $js = JavaScript::Squish->new(); $js->data($data); $js->determine_line_ending(); if ($$opts{'remove_comments'}) { if (@{$$opts{'comment_exception'}}) { $js->remove_comments(exceptions => $$opts{'comment_exception'}); } else { $js->remove_comments(); } } $js->extract_strings_and_comments(); $js->replace_white_space() if $$opts{'replace_white_space'}; $js->remove_blank_lines() if $$opts{'remove_blank_lines'}; $js->combine_concats() if $$opts{'combine_concats'}; $js->join_all() if $$opts{'join_all'}; $js->replace_extra_whitespace() if $$opts{'replace_extra_whitespace'}; $js->restore_literal_strings(); $js->restore_comments(); $js->replace_final_eol(); print OUT $js->data; close OUT if $outfile; } sub usage { my $prog_name = File::Basename::basename($0); return "Usage $prog_name [--src=] [--dest=] [--opt] (do max squishing) [--comment_exception=] (may specified multiple times) [--remove_coments] (can negate with --no_...) [--replace_white_space] (can negate with --no_...) [--remove_blank_lines] (can negate with --no_...) [--combine_concats] (can negate with --no_...) [--join_all] (can negate with --no_...) [--replace_extra_whitespace] (can negate with --no_...) [--force] (force overwrite of destination files) [--help] \n"; } sub load_options { my %opts; $opts{'comment_exception'} = []; GetOptions( "src=s" => \$opts{'src'}, "destination=s" => \$opts{'dest'}, "optimal" => \$opts{'optimal'}, "force" => \$opts{'force'}, "comment_exception=s" => $opts{'comment_exception'}, "remove_comments" => \$opts{'remove_comments'}, "replace_white_space" => \$opts{'replace_white_space'}, "remove_blank_lines" => \$opts{'remove_blank_lines'}, "combine_concats" => \$opts{'combine_concats'}, "join_all" => \$opts{'join_all'}, "replace_extra_whitespace" => \$opts{'replace_extra_whitespace'}, "no_remove_comments" => \$opts{'no_remove_comments'}, "no_replace_white_space" => \$opts{'no_replace_white_space'}, "no_remove_blank_lines" => \$opts{'no_remove_blank_lines'}, "no_combine_concats" => \$opts{'no_combine_concats'}, "no_join_all" => \$opts{'no_join_all'}, "no_replace_extra_whitespace" => \$opts{'no_replace_extra_whitespace'}, ) or die &usage(); if ($opts{'help'}) { print &usage(); exit(1); } if (! $opts{'src'}) { print "Required option [src] not specified.\n\n"; print &usage(); exit(1); } my @bool_options = qw(remove_comments replace_white_space remove_blank_lines combine_concats join_all replace_extra_whitespace); # if none of the options were requested, assume default of --opt my $something_checked; foreach my $option (@bool_options) { $something_checked++ if $opts{$option}; } # apply default optimal settings if specified # (or if nothing was checked). if ($opts{'optimal'} || (!$something_checked) ) { $opts{'remove_comments'} = 1; $opts{'replace_white_space'} = 1; $opts{'remove_blank_lines'} = 1; $opts{'combine_concats'} = 1; $opts{'join_all'} = 1; $opts{'replace_extra_whitespace'} = 1; } # apply any negatives requested foreach my $neg (@bool_options) { if ($opts{'no_'.$neg}) { $opts{$neg} = 0; } } # handle comment exceptions for (my $i=0; $i<@{$opts{'comment_exception'}}; $i++) { my $item = $opts{'comment_exception'}->[$i]; $opts{'comment_exception'}->[$i] = qr/$item/i; } return \%opts; } =head1 NAME js_compactor - Command line utility to reduce JavaScript code to as few characters as possible. =head1 USAGE js_compactor -src=source_file [OPTIONS] =head1 SYNOPSIS js_compactor [--src=source_file] [--dest=destination_file] [--opt] [--comment_exception=text] [--remove_coments] [--replace_white_space] [--remove_blank_lines] [--combine_concats] [--join_all] [--replace_extra_whitespace] [--no_remove_coments] [--no_replace_white_space] [--no_remove_blank_lines] [--no_combine_concats] [--no_join_all] [--no_replace_extra_whitespace] [--force] [--help] =head1 DESCRIPTION The "--src" option is requied. Default usage is as though you specified "--opt", for optimal settings. Setting any of the specific settings will override the default "--opt" behavior, and rules will be applied one by one. You may also specify "--opt" and then disable specific features with a "--no_option_name" style option. =over =item B<--src=filename> The source javascript filename. (REQUIRED) =item B<--dest=filename> The destination file. Defaults to output to STDOUT. =item B<--force> Force overwriting of output file if it exists. =item B<--opt> Same as B<--remove_coments --replace_white_space --remove_blank_lines --combine_concats --join_all --replace_extra_whitespace> =item B<--comment_exception=text_to_match> Comments matching the provided text will NOT be removed. The primary purpose for this is to retain copyright notices. Eg. js_compator --comment_exception=copyright -src=somefile This option may be specified multiple times. Any comment matching any of the provided strings will then be retained. It uses a case insenstive regexp for the match. This option has no effect if --no_remove_comments is specified. =item B<--remove_comments> | B<--no_remove_comments> Remove all comments from the source. =item B<--replace_white_space> | B<--no_replace_white_space> Per each line: =over =item * Removes all begining of line whitespace. =item * Removes all end of line whitespace. =item * Combined all series of whitespace into one space character (eg. s/\s+/ /g) =back Comments and string literals (if still embeded) are untouched. =item B<--remove_blank_lines> | B<--no_remove_blank_lines> Blank lines in code are removed. =item B<--combine_concats> | B<--no_combine_concats> Removes any string literal concatenations. Eg. "bob and " + "sam " + someVar; Becomes: "bob and sam " + someVar =item B<--join_all> | B<--no_join_all> Put everything on one line (retained comments may still contain new lines). =item B<--replace_extra_whitespace> | B<--no_replace_extra_whitespace> This removes any excess whitespace. Eg. if (someVar = "foo") { Becomes: if(someVar="foo"){ =back =head1 EXAMPLES The normal use is probably just for one off squishings: js_compactor --src=input_file > new_file.js If you're squishing something with a copyright, it is recommended that you retain that copyright: js_compactor --comment_exception=copyright --src=input_file > new_file.js If you want the code to still be somewhat readable, it is often helpful to retain all the line breaks: js_compactor --opt --no_join_all --comment_exception=copyright --src=input_file > new_file.js =head1 SEE ALSO L =head1 BUGS Please refer to http://developer.berlios.de/projects/jscompactor/ to report bugs. =head1 AUTHOR Joshua I. Miller =head1 COPYRIGHT AND LICENSE Copyright (c) 2005 by CallTech Communications, Inc. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut