From 6c655d57dcadd2572926e0bb6b5ddcec4c7189e0 Mon Sep 17 00:00:00 2001 From: Graham Knop Date: Thu, 30 Nov 2017 17:43:31 +0100 Subject: [PATCH] process less files in server when possible --- lib/MetaCPAN/Middleware/Static.pm | 20 ++++- lib/Plack/Middleware/Assets/Core.pm | 86 ++++++++++++++++++++ lib/Plack/Middleware/Assets/Dev.pm | 60 ++++++++++++++ lib/Plack/Middleware/Assets/FileCached.pm | 98 +++-------------------- 4 files changed, 176 insertions(+), 88 deletions(-) create mode 100644 lib/Plack/Middleware/Assets/Core.pm create mode 100644 lib/Plack/Middleware/Assets/Dev.pm diff --git a/lib/MetaCPAN/Middleware/Static.pm b/lib/MetaCPAN/Middleware/Static.pm index cfc48cc507..5c250bd474 100644 --- a/lib/MetaCPAN/Middleware/Static.pm +++ b/lib/MetaCPAN/Middleware/Static.pm @@ -71,12 +71,28 @@ sub wrap { ); } else { + my @assets = (@js_files); + if ( `lessc --version` =~ /lessc/ ) { + enable 'Assets::Dev' => ( + files => [ map "root$_", @css_files, @less_files ], + extension => 'css', + read_file => sub { + my $file = shift; + my ($root_path) = $file =~ m{^root/(.*)/}; + scalar + `lessc -s --source-map-map-inline --source-map-rootpath="/$root_path/" "$file"`; + }, + ); + } + else { + push @assets, @css_files, @less_files; + } + enable sub { my ($app) = @_; sub { my ($env) = @_; - push @{ $env->{'psgix.assets'} ||= [] }, - ( @js_files, @css_files, @less_files, ); + push @{ $env->{'psgix.assets'} ||= [] }, @assets; $app->($env); }; }; diff --git a/lib/Plack/Middleware/Assets/Core.pm b/lib/Plack/Middleware/Assets/Core.pm new file mode 100644 index 0000000000..eeedb53fa3 --- /dev/null +++ b/lib/Plack/Middleware/Assets/Core.pm @@ -0,0 +1,86 @@ +package Plack::Middleware::Assets::Core; +use Moo::Role; +use Plack::App::File; + +sub wrap { + my ( $self, $app, @args ) = @_; + if ( ref $self ) { + $self = $self->clone( app => $app ); + } + else { + $self = $self->new( { app => $app, @args } ); + } + $self->to_app; +} + +sub clone { + my $self = shift; + ( ref $self )->new( %$self, @_ ); +} + +has app => ( is => 'ro', required => 1 ); +has wrapped => ( is => 'lazy', init_arg => 0, reader => 'to_app' ); + +has files => ( is => 'ro', required => 1 ); +has read_file => ( + is => 'ro', + default => sub { + sub { + my ($file) = @_; + open my $fh, '<', $file + or die "can't open $file: $!"; + my $content = do { local $/; <$fh> }; + close $fh; + $content; + }; + } +); +has filter => ( is => 'ro' ); +has mount => ( is => 'ro', default => '_assets' ); + +has extension => ( + is => 'lazy', + default => sub { + my %uniq; + my @ext = grep { !$uniq{$_}++ } + map { /([^.]+)$/; $1 } @{ $_[0]->files }; + + if ( @ext > 1 ) { + die "extension must be specified if not all matching: @ext"; + } + $ext[0]; + }, +); + +has _asset_files => ( is => 'lazy' ); +has _static_app => ( is => 'lazy' ); + +sub _build_wrapped { + my $self = shift; + + my $mount = $self->mount; + + my $app = \&{ $self->app }; + my $mount_re = qr{^(/\Q$mount\E)(/.*)}; + + my @asset_files = @{ $self->_asset_files }; + + my $static_app = $self->_static_app; + + sub { + my ($env) = @_; + + if ( $env->{PATH_INFO} =~ $mount_re ) { + local $env->{SCRIPT_NAME} = $env->{SCRIPT_NAME} . $1; + local $env->{PATH_INFO} = $2; + my $res = $static_app->($env); + return $res + unless $res->[0] == 404; + } + + push @{ $env->{'psgix.assets'} ||= [] }, @asset_files; + $app->(@_); + }; +} + +1; diff --git a/lib/Plack/Middleware/Assets/Dev.pm b/lib/Plack/Middleware/Assets/Dev.pm new file mode 100644 index 0000000000..cb6f7e6bfb --- /dev/null +++ b/lib/Plack/Middleware/Assets/Dev.pm @@ -0,0 +1,60 @@ +package Plack::Middleware::Assets::Dev; +use Digest::SHA qw(sha1_hex); +use Moo; + +with 'Plack::Middleware::Assets::Core'; + +has _file_map => ( is => 'lazy' ); + +sub _build__file_map { + my $self = shift; + my @files = @{ $self->files }; + return { map +( sha1_hex($_) => $_ ), @files }; +} + +sub _build__static_app { + my $self = shift; + + my $file_map = $self->_file_map; + my $read_file = $self->read_file; + my $ext = $self->extension; + my $type + = Plack::MIME->mime_type( '.' . $ext ) || 'application/octet-stream'; + my $filter = $self->filter; + + sub { + my ($env) = @_; + my $path = $env->{PATH_INFO}; + + if ( $path + and $path =~ m{^/(.*)\.\Q$ext\E$} + and my $file = $file_map->{$1} ) + { + my $content = $read_file->($file); + $content = $filter->($content) + if $filter; + return [ + 200, + [ + 'Content-Type' => $type, + 'Cache-Control', 'no-cache', + ], + [$content] + ]; + } + return [ 404, [], [] ]; + }; +} + +sub _build__asset_files { + my $self = shift; + my $extension = $self->extension; + my $mount = $self->mount; + my $file_map = $self->_file_map; + return [ + map "/$mount/$_.$extension", + sort { $file_map->{$a} cmp $file_map->{$b} } keys %$file_map + ]; +} + +1; diff --git a/lib/Plack/Middleware/Assets/FileCached.pm b/lib/Plack/Middleware/Assets/FileCached.pm index 63322dcdd5..8b7a011607 100644 --- a/lib/Plack/Middleware/Assets/FileCached.pm +++ b/lib/Plack/Middleware/Assets/FileCached.pm @@ -1,59 +1,10 @@ package Plack::Middleware::Assets::FileCached; -use Moo; use File::Temp (); -use Plack::App::File; use Digest::SHA qw(sha1_hex); use File::Path (); +use Moo; -sub wrap { - my ( $self, $app, @args ) = @_; - if ( ref $self ) { - $self = $self->clone( app => $app ); - } - else { - $self = $self->new( { app => $app, @args } ); - } - $self->to_app; -} - -sub clone { - my $self = shift; - ( ref $self )->new( %$self, @_ ); -} - -has app => ( is => 'ro', required => 1 ); -has wrapped => ( is => 'lazy', init_arg => 0, reader => 'to_app' ); - -has files => ( is => 'ro', required => 1 ); -has read_file => ( - is => 'ro', - default => sub { - sub { - my ($file) = @_; - open my $fh, '<', $file - or die "can't open $file: $!"; - my $content = do { local $/; <$fh> }; - close $fh; - $content; - }; - } -); -has filter => ( is => 'ro' ); -has mount => ( is => 'ro', default => '_assets' ); - -has extension => ( - is => 'lazy', - default => sub { - my %uniq; - my @ext = grep { !$uniq{$_}++ } - map { /([^.]+)$/; $1 } @{ $_[0]->files }; - - if ( @ext > 1 ) { - die "extension must be specified if not all matching: @ext"; - } - $ext[0]; - } -); +with 'Plack::Middleware::Assets::Core'; has cache_dir => ( is => 'ro', @@ -69,53 +20,29 @@ has cache_dir => ( }, ); -has _asset_files => ( is => 'lazy' ); - -sub _build_wrapped { - my $self = shift; - +sub _build__static_app { + my $self = shift; my $cache_dir = $self->cache_dir; - my $mount = $self->mount; - - my $file_app = Plack::App::File->new( root => "$cache_dir" )->to_app; - my $static_app = sub { + my $file_app = Plack::App::File->new( root => "$cache_dir" )->to_app; + sub { my $res = &$file_app; push @{ $res->[1] }, 'Cache-Control', 'max-age=31556926'; return $res; }; - - my $app = \&{ $self->app }; - my $mount_re = qr{^(/\Q$mount\E)(/.*)}; - - my @asset_files = @{ $self->_asset_files }; - - sub { - my ($env) = @_; - - if ( $env->{PATH_INFO} =~ $mount_re ) { - local $env->{SCRIPT_NAME} = $env->{SCRIPT_NAME} . $1; - local $env->{PATH_INFO} = $2; - my $res = $static_app->($env); - return $res - unless $res->[0] == 404; - } - - push @{ $env->{'psgix.assets'} ||= [] }, @asset_files; - $app->(@_); - }; } sub _build__asset_files { my $self = shift; - my $read_file = $self->read_file; my @files = @{ $self->files }; - my @assets; - my $content = join "\n", map { $read_file->($_) } @files; + my $extension = $self->extension; + my $mount = $self->mount; + my $read_file = $self->read_file; + my $content = join "\n", map { $read_file->($_) } @files; if ( my $filter = $self->filter ) { $content = $filter->($content); } my $key = sha1_hex($content); - my $file = "$key." . $self->extension; + my $file = "$key." . $extension; my $disk_file = $self->cache_dir . "/$file"; if ( !-e $disk_file ) { open my $fh, '>', $disk_file @@ -123,8 +50,7 @@ sub _build__asset_files { print {$fh} $content; close $fh; } - push @assets, '/' . $self->mount . "/$file"; - \@assets; + ["/$mount/$file"]; } 1;