#!/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 = <IN>; }
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=<source file>]
[--dest=<destination file>]
[--opt] (do max squishing)
[--comment_exception=<copyright>] (may specified multiple times)
[--remove_comments] (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'},
"help" => \$opts{'help'},
"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_comments]
[--replace_white_space]
[--remove_blank_lines]
[--combine_concats]
[--join_all]
[--replace_extra_whitespace]
[--no_remove_comments]
[--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 required.
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_comments --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_compactor --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<JavaScript::Squish>
=head1 BUGS
Please refer to http://developer.berlios.de/projects/jscompactor/ to report bugs.
=head1 AUTHOR
Joshua I. Miller <jmiller@puriifeddata.net>
=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