Commit 1bead98c authored by Byron Jones's avatar Byron Jones

Bug 1064395: concatenate and slightly minify javascript files

r=dkl,a=glob
parent 2288b468
...@@ -26,6 +26,8 @@ use Memoize; ...@@ -26,6 +26,8 @@ use Memoize;
bz_locations bz_locations
CONCATENATE_ASSETS
IS_NULL IS_NULL
NOT_NULL NOT_NULL
...@@ -210,6 +212,11 @@ use constant REST_DOC => "http://www.bugzilla.org/docs/tip/en/html/api/"; ...@@ -210,6 +212,11 @@ use constant REST_DOC => "http://www.bugzilla.org/docs/tip/en/html/api/";
use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml';
use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir. use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir.
# When true CSS and JavaScript assets will be concatanted and minified at
# run-time, to reduce the number of requests required to render a page.
# Setting this to a false value can help debugging.
use constant CONCATENATE_ASSETS => 1;
# These are unique values that are unlikely to match a string or a number, # These are unique values that are unlikely to match a string or a number,
# to be used in criteria for match() functions and other things. They start # to be used in criteria for match() functions and other things. They start
# and end with spaces because most Bugzilla stuff has trim() called on it, # and end with spaces because most Bugzilla stuff has trim() called on it,
......
...@@ -31,6 +31,7 @@ use File::Path; ...@@ -31,6 +31,7 @@ use File::Path;
use File::Basename; use File::Basename;
use File::Copy qw(move); use File::Copy qw(move);
use File::Spec; use File::Spec;
use File::Slurp;
use IO::File; use IO::File;
use POSIX (); use POSIX ();
...@@ -367,7 +368,7 @@ EOT ...@@ -367,7 +368,7 @@ EOT
"$assetsdir/.htaccess" => { perms => WS_SERVE, contents => <<EOT "$assetsdir/.htaccess" => { perms => WS_SERVE, contents => <<EOT
# Allow access to .css files # Allow access to .css files
<FilesMatch \\.css\$> <FilesMatch \\.(css|js)\$>
Allow from all Allow from all
</FilesMatch> </FilesMatch>
...@@ -410,6 +411,7 @@ sub update_filesystem { ...@@ -410,6 +411,7 @@ sub update_filesystem {
my $datadir = bz_locations->{'datadir'}; my $datadir = bz_locations->{'datadir'};
my $graphsdir = bz_locations->{'graphsdir'}; my $graphsdir = bz_locations->{'graphsdir'};
my $assetsdir = bz_locations->{'assetsdir'};
# If the graphs/ directory doesn't exist, we're upgrading from # If the graphs/ directory doesn't exist, we're upgrading from
# a version old enough that we need to update the $datadir/mining # a version old enough that we need to update the $datadir/mining
# format. # format.
...@@ -450,6 +452,13 @@ sub update_filesystem { ...@@ -450,6 +452,13 @@ sub update_filesystem {
_rename_file($oldparamsfile, "$datadir/$oldparamsfile"); _rename_file($oldparamsfile, "$datadir/$oldparamsfile");
} }
# Remove old assets htaccess file to force recreation with correct values.
if (-e "$assetsdir/.htaccess") {
if (read_file("$assetsdir/.htaccess") =~ /<FilesMatch \\\.css\$>/) {
unlink("$assetsdir/.htaccess");
}
}
_create_files(%files); _create_files(%files);
if ($params->{index_html}) { if ($params->{index_html}) {
_create_files(%{$fs->{index_html}}); _create_files(%{$fs->{index_html}});
...@@ -493,7 +502,7 @@ EOT ...@@ -493,7 +502,7 @@ EOT
_remove_empty_css_files(); _remove_empty_css_files();
_convert_single_file_skins(); _convert_single_file_skins();
_remove_dynamic_css_files(); _remove_dynamic_assets();
} }
sub _remove_empty_css_files { sub _remove_empty_css_files {
...@@ -538,10 +547,14 @@ sub _convert_single_file_skins { ...@@ -538,10 +547,14 @@ sub _convert_single_file_skins {
} }
} }
# delete all automatically generated css files to force recreation at the next # delete all automatically generated css/js files to force recreation at the
# request. # next request.
sub _remove_dynamic_css_files { sub _remove_dynamic_assets {
foreach my $file (glob(bz_locations()->{assetsdir} . '/*.css')) { my @files = (
glob(bz_locations()->{assetsdir} . '/*.css'),
glob(bz_locations()->{assetsdir} . '/*.js'),
);
foreach my $file (@files) {
unlink($file); unlink($file);
} }
......
...@@ -530,7 +530,7 @@ sub _concatenate_css { ...@@ -530,7 +530,7 @@ sub _concatenate_css {
write_file($file, $content); write_file($file, $content);
} }
$file =~ s/^\Q$cgi_path\E\///; $file =~ s/^\Q$cgi_path\E\///o;
return mtime_filter($file); return mtime_filter($file);
} }
...@@ -543,6 +543,54 @@ sub _css_url_rewrite { ...@@ -543,6 +543,54 @@ sub _css_url_rewrite {
return 'url(../../' . dirname($source) . '/' . $url . ')'; return 'url(../../' . dirname($source) . '/' . $url . ')';
} }
sub _concatenate_js {
return @_ unless CONCATENATE_ASSETS;
my ($sources) = @_;
return [] unless $sources && ref($sources);
my %files =
map {
(my $file = $_) =~ s/(^[^\?]+)\?.+/$1/;
$_ => $file;
} @$sources;
my $cgi_path = bz_locations()->{cgi_path};
my $skins_path = bz_locations()->{assetsdir};
# build minified files
my @minified;
foreach my $source (@$sources) {
next unless -e "$cgi_path/$files{$source}";
my $file = $skins_path . '/' . md5_hex($source) . '.js';
if (!-e $file) {
my $content = read_file("$cgi_path/$files{$source}");
# minimal minification
$content =~ s#/\*.*?\*/##sg; # block comments
$content =~ s#(^ +| +$)##gm; # leading/trailing spaces
$content =~ s#^//.+$##gm; # single line comments
$content =~ s#\n{2,}#\n#g; # blank lines
$content =~ s#(^\s+|\s+$)##g; # whitespace at the start/end of file
write_file($file, "/* $files{$source} */\n" . $content . "\n");
}
push @minified, $file;
}
# concat files
my $file = $skins_path . '/' . md5_hex(join(' ', @$sources)) . '.js';
if (!-e $file) {
my $content = '';
foreach my $source (@minified) {
$content .= read_file($source);
}
write_file($file, $content);
}
$file =~ s/^\Q$cgi_path\E\///o;
return [ $file ];
}
# YUI dependency resolution # YUI dependency resolution
sub yui_resolve_deps { sub yui_resolve_deps {
my ($yui, $yui_deps) = @_; my ($yui, $yui_deps) = @_;
...@@ -1054,6 +1102,7 @@ sub create { ...@@ -1054,6 +1102,7 @@ sub create {
'css_files' => \&css_files, 'css_files' => \&css_files,
yui_resolve_deps => \&yui_resolve_deps, yui_resolve_deps => \&yui_resolve_deps,
concatenate_js => \&_concatenate_js,
# All classifications (sorted by sortkey, name) # All classifications (sorted by sortkey, name)
'all_classifications' => sub { 'all_classifications' => sub {
......
...@@ -90,8 +90,16 @@ ...@@ -90,8 +90,16 @@
[% SET yui = yui_resolve_deps(yui, yui_deps) %] [% SET yui = yui_resolve_deps(yui, yui_deps) %]
[% SET css_sets = css_files(style_urls, yui, yui_css) %] [% SET css_sets = css_files(style_urls, yui, yui_css) %]
<link href="[% css_sets.unified_standard_skin FILTER html %]" [% IF constants.CONCATENATE_ASSETS %]
rel="stylesheet" type="text/css"> [% PROCESS format_css_link asset_url = css_sets.unified_standard_skin %]
[% ELSE %]
[% FOREACH asset_url = css_sets.standard %]
[% PROCESS format_css_link %]
[% END %]
[% FOREACH asset_url = css_sets.skin %]
[% PROCESS format_css_link %]
[% END %]
[% END %]
[% IF style %] [% IF style %]
<style type="text/css"> <style type="text/css">
...@@ -100,8 +108,13 @@ ...@@ -100,8 +108,13 @@
[% END %] [% END %]
[% IF css_sets.unified_custom %] [% IF css_sets.unified_custom %]
<link href="[% css_sets.unified_custom FILTER html %]" [% IF constants.CONCATENATE_ASSETS %]
rel="stylesheet" type="text/css"> [% PROCESS format_css_link asset_url = css_sets.unified_custom %]
[% ELSE %]
[% FOREACH asset_rul = css_sets.custom %]
[% PROCESS format_css_link %]
[% END %]
[% END %]
[% END %] [% END %]
[%# YUI Scripts %] [%# YUI Scripts %]
...@@ -110,7 +123,7 @@ ...@@ -110,7 +123,7 @@
[% END %] [% END %]
[% starting_js_urls.push('js/global.js') %] [% starting_js_urls.push('js/global.js') %]
[% FOREACH javascript_url = starting_js_urls %] [% FOREACH asset_url = concatenate_js(starting_js_urls) %]
[% PROCESS format_js_link %] [% PROCESS format_js_link %]
[% END %] [% END %]
...@@ -180,7 +193,7 @@ ...@@ -180,7 +193,7 @@
// --> // -->
</script> </script>
[% FOREACH javascript_url = javascript_urls %] [% FOREACH asset_url = concatenate_js(javascript_urls) %]
[% PROCESS format_js_link %] [% PROCESS format_js_link %]
[% END %] [% END %]
...@@ -251,6 +264,10 @@ ...@@ -251,6 +264,10 @@
<div id="message">[% message %]</div> <div id="message">[% message %]</div>
[% END %] [% END %]
[% BLOCK format_css_link %]
<link href="[% asset_url FILTER html %]" rel="stylesheet" type="text/css">
[% END %]
[% BLOCK format_js_link %] [% BLOCK format_js_link %]
<script type="text/javascript" src="[% javascript_url FILTER mtime FILTER html %]"></script> <script type="text/javascript" src="[% asset_url FILTER mtime FILTER html %]"></script>
[% END %] [% END %]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment