slight_assets 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/generators/slight_assets/slight_assets_generator.rb +28 -0
- data/lib/slight_assets/base.rb +7 -0
- data/lib/slight_assets/js_reducer.rb +85 -0
- data/lib/slight_assets/rails.rb +79 -0
- data/lib/slight_assets/rake_tasks.rb +112 -0
- data/lib/slight_assets/settings/default_config.yml +43 -0
- data/lib/slight_assets/settings.rb +57 -0
- data/lib/slight_assets/util.rb +243 -0
- data/lib/slight_assets/version.rb +6 -0
- data/lib/slight_assets.rb +1 -0
- data/templates/install/config/assets.yml +42 -0
- data/templates/install/config/initializers/assets.rb +2 -0
- metadata +154 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
class SlightAssetsGenerator < ::Rails::Generator::Base
|
4
|
+
def manifest
|
5
|
+
record do |m|
|
6
|
+
m.file "config/assets.yml", "config/assets.yml"
|
7
|
+
m.file "config/initializers/assets.rb", "config/initializers/assets.rb"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.gem_root
|
12
|
+
File.expand_path('../../../', __FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.source_root
|
16
|
+
File.join(gem_root, 'templates', 'install')
|
17
|
+
end
|
18
|
+
|
19
|
+
def source_root
|
20
|
+
self.class.source_root
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def banner
|
26
|
+
"Usage: #{$0} slight_assets"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module SlightAssets
|
2
|
+
class JsReducer
|
3
|
+
BYTE_RANGE = 32..127
|
4
|
+
BASE_NUMBER = BYTE_RANGE.count
|
5
|
+
FETCH_RANGE = BASE_NUMBER ** 2
|
6
|
+
LOOKUP_CHAR = "`"
|
7
|
+
ESCAPABLE_CHARS = ["\\", "\"", "\r", "\n"].freeze
|
8
|
+
|
9
|
+
def compress(js_content)
|
10
|
+
content = reduce(js_content)
|
11
|
+
return js_content if content == js_content
|
12
|
+
func = 'function(x){var b="",p=0,c,l,L=x.length,m="`";while(p<L)b+=(c=x[p++])!=m?c:(l=x.charCodeAt(p++)-31)>1?b.substr(b.length-x.charCodeAt(p++)*96-x.charCodeAt(p++)+3104-l,l):m;return b}'
|
13
|
+
content = content.gsub(/([\\"])/, '\\\\\\1').gsub(/\r/, "\\r").gsub(/\n/, "\\n")
|
14
|
+
"eval((#{func})(\"#{content}\"))"
|
15
|
+
end
|
16
|
+
|
17
|
+
def reduce(js_content)
|
18
|
+
max_cropsize = BASE_NUMBER
|
19
|
+
content = str_as_utf8(js_content)
|
20
|
+
return js_content if content.nil?
|
21
|
+
output, index, limit = "", -1, content.size
|
22
|
+
while (index+=1) < limit
|
23
|
+
i, chars = -1, str_as_utf8("")
|
24
|
+
cropindex, cropsize, s = nil, 0, 0
|
25
|
+
while (i+=1) < max_cropsize && index + i < limit
|
26
|
+
chars << (c = content[index+i])
|
27
|
+
next if s < 5 && (s += (ESCAPABLE_CHARS.include?(c) ? 2 : c.bytesize) ) < 5
|
28
|
+
max_start = index - chars.size
|
29
|
+
start = [0, max_start - FETCH_RANGE].max
|
30
|
+
if (pos = content.index(chars, start)) && pos <= max_start
|
31
|
+
cropindex, cropsize = pos, chars.size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
if cropindex.nil?
|
35
|
+
output << content[index]
|
36
|
+
output << " " if content[index] == LOOKUP_CHAR
|
37
|
+
else
|
38
|
+
max_start = [0, index - cropsize].max
|
39
|
+
chars = str_as_utf8(content[cropindex, cropsize])
|
40
|
+
cropindex = pos while (pos = content.index(chars, cropindex + 1)) && pos <= max_start
|
41
|
+
output << LOOKUP_CHAR
|
42
|
+
output << BYTE_RANGE.last - max_cropsize + cropsize
|
43
|
+
offset = (index - cropindex) - cropsize
|
44
|
+
output << BYTE_RANGE.first + (offset / BASE_NUMBER)
|
45
|
+
output << BYTE_RANGE.first + (offset % BASE_NUMBER)
|
46
|
+
index += cropsize - 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
output
|
50
|
+
end
|
51
|
+
|
52
|
+
if RUBY_VERSION =~ /\A1\.[0-8]/
|
53
|
+
begin
|
54
|
+
require "utf8"
|
55
|
+
def str_as_utf8(str)
|
56
|
+
str.as_utf8
|
57
|
+
end
|
58
|
+
rescue LoadError
|
59
|
+
def str_as_utf8(str)
|
60
|
+
a = str.is_a?(Array) ? str : str.scan(/./mu)
|
61
|
+
class << a
|
62
|
+
def index(chars, start)
|
63
|
+
return nil if start >= size
|
64
|
+
i, s, cs = start - 1, size, chars.size
|
65
|
+
while (i+=1) < s
|
66
|
+
next if self[i] != chars[0]
|
67
|
+
return i if cs == 1
|
68
|
+
j = 0
|
69
|
+
while (j+=1) < cs && i + j < s
|
70
|
+
break if self[i+j] != chars[j]
|
71
|
+
return i if j + 1 == cs
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
a
|
77
|
+
end
|
78
|
+
end
|
79
|
+
else
|
80
|
+
def str_as_utf8(str)
|
81
|
+
str
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SlightAssets
|
2
|
+
module Rails
|
3
|
+
def init!
|
4
|
+
ActionView::Helpers::AssetTagHelper.send :include, HelperMethods
|
5
|
+
end
|
6
|
+
module_function :init!
|
7
|
+
|
8
|
+
module HelperMethods
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
alias_method_chain :write_asset_file_contents, :static_compressed_file
|
12
|
+
alias_method_chain :javascript_src_tag, :static_compressed_file
|
13
|
+
alias_method_chain :stylesheet_tag, :static_compressed_file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def write_asset_file_contents_with_static_compressed_file(joined_asset_path, asset_paths)
|
20
|
+
minify = false
|
21
|
+
asset_paths = asset_paths.map do |asset_path|
|
22
|
+
p = asset_path.to_s
|
23
|
+
if p =~ /\.min\./
|
24
|
+
asset_path
|
25
|
+
else
|
26
|
+
p = p.gsub(/\.(js|css)(\?\w+)?\z/, '.min.\1\2')
|
27
|
+
if ::File.exists?(asset_file_path(p))
|
28
|
+
p
|
29
|
+
else
|
30
|
+
minify = true
|
31
|
+
asset_path
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
minified_joined_asset_path = joined_asset_path.gsub(/(?:\.min)?\.(js|css)\z/, '.min.\1')
|
36
|
+
already_existed = File.exists?(minified_joined_asset_path)
|
37
|
+
r = write_asset_file_contents_without_static_compressed_file(minify ? joined_asset_path : minified_joined_asset_path, asset_paths)
|
38
|
+
if minify && ! already_existed
|
39
|
+
SlightAssets::Util.async_write_static_compressed_file(joined_asset_path)
|
40
|
+
else
|
41
|
+
SlightAssets::Util.write_static_gzipped_file(minified_joined_asset_path)
|
42
|
+
end
|
43
|
+
r
|
44
|
+
end
|
45
|
+
|
46
|
+
def javascript_src_tag_with_static_compressed_file(source, options)
|
47
|
+
unless source =~ /\A(?:\w+:)?\/\//
|
48
|
+
asset_path = path_to_javascript(source).split('?').first
|
49
|
+
file_path = asset_file_path(asset_path)
|
50
|
+
if asset_path.end_with?(".js") && asset_path !~ /\.min\./
|
51
|
+
compressed_path = asset_path.gsub(/\.js\z/, '.min.js')
|
52
|
+
if ::File.exists?(asset_file_path(compressed_path))
|
53
|
+
source = compressed_path unless ::File.exists?("#{file_path}.locked")
|
54
|
+
elsif ::File.exists?(file_path)
|
55
|
+
SlightAssets::Util.async_write_static_compressed_file(file_path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
javascript_src_tag_without_static_compressed_file(source, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def stylesheet_tag_with_static_compressed_file(source, options)
|
63
|
+
unless source =~ /\A(?:\w+:)?\/\//
|
64
|
+
asset_path = path_to_stylesheet(source).split('?').first
|
65
|
+
file_path = asset_file_path(asset_path)
|
66
|
+
if asset_path.end_with?(".css") && asset_path !~ /\.min\./
|
67
|
+
compressed_path = asset_path.gsub(/\.css\z/, '.min.css')
|
68
|
+
if ::File.exists?(asset_file_path(compressed_path))
|
69
|
+
source = compressed_path unless ::File.exists?("#{file_path}.locked")
|
70
|
+
elsif ::File.exists?(file_path)
|
71
|
+
SlightAssets::Util.async_write_static_compressed_file(file_path)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
stylesheet_tag_without_static_compressed_file(source, options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
namespace :asset do
|
2
|
+
namespace :compress do
|
3
|
+
task :pre => :environment do
|
4
|
+
require "slight_assets"
|
5
|
+
class AssetWriter
|
6
|
+
include ::SlightAssets::Util
|
7
|
+
attr_reader :image_report
|
8
|
+
def initialize
|
9
|
+
@image_report = {}
|
10
|
+
end
|
11
|
+
def write_compressed_file(file)
|
12
|
+
write_static_compressed_file(file)
|
13
|
+
end
|
14
|
+
def js_list
|
15
|
+
unless defined?(@js_list)
|
16
|
+
@js_list = []
|
17
|
+
(SlightAssets::Cfg.minify_assets? || []).each do |mask|
|
18
|
+
if mask =~ /\A([+-])?\s*(.*\.js)\z/
|
19
|
+
oper = $1 || "+"
|
20
|
+
files_mask = [::Rails.root, "public", "javascripts", $2]
|
21
|
+
@js_list = @js_list.send(oper, FileList[File.join(*files_mask)])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
files_mask = [::Rails.root, "public", "javascripts", "**", "*.min.js"]
|
25
|
+
@js_list = (@js_list - FileList[File.join(*files_mask)]).uniq.sort
|
26
|
+
end
|
27
|
+
@js_list
|
28
|
+
end
|
29
|
+
def css_list
|
30
|
+
unless defined?(@css_list)
|
31
|
+
@css_list = []
|
32
|
+
(SlightAssets::Cfg.minify_assets? || []).each do |mask|
|
33
|
+
if mask =~ /\A([+-])?\s*(.*\.css)\z/
|
34
|
+
oper = $1 || "+"
|
35
|
+
files_mask = [::Rails.root, "public", "stylesheets", $2]
|
36
|
+
@css_list = @css_list.send(oper, FileList[File.join(*files_mask)])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
files_mask = [::Rails.root, "public", "stylesheets", "**", "*.min.css"]
|
40
|
+
@css_list = (@css_list - FileList[File.join(*files_mask)]).uniq.sort
|
41
|
+
end
|
42
|
+
@css_list
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Compress all JS files from your Rails application"
|
48
|
+
task :js => :pre do
|
49
|
+
puts "=" * 80
|
50
|
+
if SlightAssets::Util.js_compressor.nil?
|
51
|
+
puts "WARN: No JS compressor was found"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
writer = AssetWriter.new
|
55
|
+
jslist = writer.js_list
|
56
|
+
originalsize, compressedsize = 0, 0
|
57
|
+
start_time = Time.now
|
58
|
+
jslist.each do |jsfile|
|
59
|
+
print "Compressing #{jsfile[(Rails.root.to_s.size+1)..-1]}"
|
60
|
+
time = Time.now
|
61
|
+
minfile = writer.write_compressed_file(jsfile).chomp(".gz")
|
62
|
+
originalsize += jssize = File.size(jsfile)
|
63
|
+
compressedsize += minsize = File.size(minfile)
|
64
|
+
percent = jssize.zero? ? 100 : (100.0 * minsize / jssize).round
|
65
|
+
puts " (#{Time.now - time}s)\n -> #{jssize} bytes to #{minsize} bytes (#{percent}% size)"
|
66
|
+
end
|
67
|
+
if jslist.any?
|
68
|
+
puts "Total #{jslist.size} JS files were compressed"
|
69
|
+
percent = originalsize.zero? ? 100 : (100.0 * compressedsize / originalsize).round
|
70
|
+
puts " -> reduced from #{originalsize} bytes to #{compressedsize} bytes (#{percent}% size total, #{originalsize - compressedsize} bytes saved)"
|
71
|
+
end
|
72
|
+
puts "Elapsed time: #{Time.now - start_time}s"
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "Compress all CSS files from your Rails application"
|
76
|
+
task :css => :pre do
|
77
|
+
puts "=" * 80
|
78
|
+
if SlightAssets::Util.css_compressor.nil?
|
79
|
+
puts "WARN: No CSS compressor was found"
|
80
|
+
exit 1
|
81
|
+
end
|
82
|
+
writer = AssetWriter.new
|
83
|
+
csslist = writer.css_list
|
84
|
+
start_time = Time.now
|
85
|
+
csslist.each do |cssfile|
|
86
|
+
print "Compressing #{cssfile[(Rails.root.to_s.size+1)..-1]}"
|
87
|
+
time = Time.now
|
88
|
+
minfile = writer.write_compressed_file(cssfile).chomp(".gz")
|
89
|
+
csssize = File.size(cssfile)
|
90
|
+
minsize = File.size(minfile)
|
91
|
+
percent = csssize.zero? ? 100 : (100.0 * minsize / csssize).round
|
92
|
+
puts " (#{Time.now - time}s)\n -> #{csssize} bytes to #{minsize} bytes (#{percent}% size)"
|
93
|
+
end
|
94
|
+
puts "Total: #{csslist.size} CSS files were compressed" if csslist.any?
|
95
|
+
puts "Elapsed time: #{Time.now - start_time}s"
|
96
|
+
if writer.image_report.any?
|
97
|
+
puts "=" * 80
|
98
|
+
writer.image_report.each_pair do |image_file, report|
|
99
|
+
refs = report[:found_in].size
|
100
|
+
puts "Image: #{image_file[(Rails.public_path.size)..-1]} (#{"\033[1;31m" unless refs == 1}#{refs} occurrence#{"s" unless refs == 1}\033[0m)"
|
101
|
+
report[:found_in].each_pair do |css_file, count|
|
102
|
+
mode = report[:exists] ? (report[:embeddable] && count == 1 ? "embedded" : "\033[0;33mnot embedded\033[0m") : "\033[0;31mnot found\033[0m"
|
103
|
+
puts " found in #{css_file[(Rails.public_path.size)..-1]} (#{count} ref#{"s" unless count == 1}, #{mode})"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Compress all JS and CSS files from your Rails application"
|
111
|
+
task :compress => ["compress:js", "compress:css"]
|
112
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
slight_asset: &slight_asset
|
2
|
+
|
3
|
+
# Maximum embedded file size
|
4
|
+
# The limit file size to decide whether or not to embed
|
5
|
+
# Must be less than or equal to "32kB"
|
6
|
+
maximum_embedded_file_size: "32kB"
|
7
|
+
|
8
|
+
# Scope of assets to minify in batch process (rake).
|
9
|
+
# The prefix "-" indicates the files to exclude from minifying.
|
10
|
+
# Keep in mind that the sequence matters.
|
11
|
+
minify_assets:
|
12
|
+
- **/*.js
|
13
|
+
- -**/ckeditor/**/*.js
|
14
|
+
- **/*.css
|
15
|
+
# in the end, the masks below will be used for cleaning (even if omitted).
|
16
|
+
- -**/*.min.js
|
17
|
+
- -**/*.min.css
|
18
|
+
|
19
|
+
# Absolute MHTML base href (Optional)
|
20
|
+
# If present, the CSS images that was referenced twice or more
|
21
|
+
# will be embedded inside a MHTML block
|
22
|
+
#mhtml_base_href: "http://localhost:3000"
|
23
|
+
|
24
|
+
# JavaScript Reducer (Optional)
|
25
|
+
# This mode enables obfuscation for JavaScript compression, reducing
|
26
|
+
# even more the file size.
|
27
|
+
# Sets the JavaScript reduce mode:
|
28
|
+
# "rake" - Reduce only when executing rake task (asset:compress:js)
|
29
|
+
# true - Reduce at runtime or when executing rake
|
30
|
+
# false - Don't reduce JavaScript's
|
31
|
+
js_reducer: false
|
32
|
+
|
33
|
+
development:
|
34
|
+
slight_asset:
|
35
|
+
<<: *slight_asset
|
36
|
+
|
37
|
+
test:
|
38
|
+
slight_asset:
|
39
|
+
<<: *slight_asset
|
40
|
+
|
41
|
+
production:
|
42
|
+
slight_asset:
|
43
|
+
<<: *slight_asset
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module SlightAssets
|
4
|
+
class Config
|
5
|
+
attr_reader :config
|
6
|
+
def initialize
|
7
|
+
@config = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *args)
|
11
|
+
sname = name.to_s
|
12
|
+
case sname
|
13
|
+
when /\?$/
|
14
|
+
@config[$`]
|
15
|
+
when /\=$/
|
16
|
+
@config[$`] = args.first
|
17
|
+
else
|
18
|
+
return @config[sname] if @config.has_key?(sname)
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_config(path)
|
24
|
+
return @config unless File.exists? path
|
25
|
+
hash = YAML.load_file(path)
|
26
|
+
hash = hash[::Rails.env] || hash if defined?(::Rails)
|
27
|
+
@config.merge! hash["slight_asset"] if hash.has_key?("slight_asset")
|
28
|
+
rescue TypeError => e
|
29
|
+
puts "could not load #{path}: #{e.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def maximum_embedded_file_size
|
33
|
+
unless defined?(@maximum_embedded_file_size)
|
34
|
+
c = @config["maximum_embedded_file_size"]
|
35
|
+
if c.is_a?(Fixnum)
|
36
|
+
@maximum_embedded_file_size = c
|
37
|
+
elsif c =~ /\A(\d+)\s*(\w+)\z/
|
38
|
+
c = $1.to_i
|
39
|
+
case $2
|
40
|
+
when "kB", "kb"
|
41
|
+
c = c * 1_024
|
42
|
+
end
|
43
|
+
@maximum_embedded_file_size = [c, 32 * 1_024].min
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@maximum_embedded_file_size
|
47
|
+
end
|
48
|
+
|
49
|
+
def url_matcher
|
50
|
+
/url\(['"]?(.*?)(?:\?\d+)?['"]?\)/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Cfg = Config.new
|
55
|
+
Cfg.load_config File.expand_path(File.join(*%w[.. settings default_config.yml]), __FILE__)
|
56
|
+
Cfg.load_config ::Rails.root.join(*%w[config assets.yml]) if defined?(::Rails)
|
57
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "zlib"
|
3
|
+
|
4
|
+
module SlightAssets
|
5
|
+
module Util
|
6
|
+
def write_static_gzipped_file(file_path, content = nil)
|
7
|
+
return file_path if file_path.nil? || file_path.end_with?(".gz") || ! File.exists?(file_path)
|
8
|
+
zip_path = "#{file_path}.gz"
|
9
|
+
return zip_path if File.exists?(zip_path)
|
10
|
+
content = File.read(file_path) if content.nil?
|
11
|
+
Zlib::GzipWriter.open(zip_path, Zlib::BEST_COMPRESSION) {|f| f.write(content) }
|
12
|
+
# Set mtime to the latest of the file to allow for
|
13
|
+
# consistent ETag without a shared filesystem.
|
14
|
+
mt = File.mtime(file_path)
|
15
|
+
File.utime(mt, mt, zip_path)
|
16
|
+
zip_path
|
17
|
+
end
|
18
|
+
module_function :write_static_gzipped_file
|
19
|
+
|
20
|
+
def write_static_minified_asset(file_path)
|
21
|
+
return file_path if file_path.nil? || file_path =~ /\.min\./ || file_path !~ /\.(?:js|css)\z/ || ! File.exists?(file_path)
|
22
|
+
min_path = file_path.gsub(/\.(js|css)\z/, '.min.\1')
|
23
|
+
compressor = nil
|
24
|
+
case extension = $1
|
25
|
+
when "css"
|
26
|
+
compressor = css_compressor
|
27
|
+
when "js"
|
28
|
+
compressor = js_compressor
|
29
|
+
end
|
30
|
+
return file_path if compressor.nil?
|
31
|
+
content = File.read(file_path)
|
32
|
+
c = compressor.compress(content)
|
33
|
+
content = c if c.bytesize < content.bytesize
|
34
|
+
content = embed_images(content, min_path) if extension == "css"
|
35
|
+
reduced_content = content
|
36
|
+
if extension == "js"
|
37
|
+
reduced_content = js_reduce(content)
|
38
|
+
reduced_content = content if reduced_content.bytesize > content.bytesize
|
39
|
+
end
|
40
|
+
File.open(min_path, "w") { |f| f.write(reduced_content) }
|
41
|
+
mt = File.mtime(file_path)
|
42
|
+
File.utime(mt, mt, min_path)
|
43
|
+
[min_path, content]
|
44
|
+
end
|
45
|
+
module_function :write_static_minified_asset
|
46
|
+
|
47
|
+
def js_reduce(content)
|
48
|
+
unless (Cfg.js_reducer? == "rake" && is_rake?) || Cfg.js_reducer? == true
|
49
|
+
content
|
50
|
+
else
|
51
|
+
JsReducer.new.compress(content)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
module_function :js_reduce
|
55
|
+
|
56
|
+
def embed_images(content, file_path)
|
57
|
+
return content if file_path =~ /\A(?:\w+:)?\/\//
|
58
|
+
images = extract_images(content, file_path)
|
59
|
+
file_url = asset_url(file_path)
|
60
|
+
image_contents = {}
|
61
|
+
multipart = ["/*\r\nContent-Type: multipart/related; boundary=\"MHTML_IMAGES\"\r\n"]
|
62
|
+
replace_imports = Cfg.replace_css_imports?
|
63
|
+
content = content.gsub(Cfg.url_matcher) do |url_match|
|
64
|
+
image_path = $1
|
65
|
+
quot = (url_match =~ /"/ ? "\"" : "")
|
66
|
+
if image_path =~ /\A(?:[\\\/]|\w+:)/
|
67
|
+
url_match
|
68
|
+
elsif image_path =~ /(\.min)?\.css\z/ && replace_imports
|
69
|
+
if $1
|
70
|
+
url_match
|
71
|
+
elsif File.exists?(asset_expand_path(image_path, file_path))
|
72
|
+
"url(#{quot}#{image_path.gsub(/\.css\z/, ".min.css")}#{quot})"
|
73
|
+
else
|
74
|
+
url_match
|
75
|
+
end
|
76
|
+
else
|
77
|
+
image_file_path = asset_expand_path(image_path, file_path)
|
78
|
+
if (mt = image_mime_type(image_path)) &&
|
79
|
+
(encode64 = encoded_file_contents(image_file_path, file_path))
|
80
|
+
if image_contents[image_file_path].nil?
|
81
|
+
part_name = "img#{multipart.size}_#{File.basename(image_file_path)}"
|
82
|
+
image_contents[image_file_path] = [part_name, encode64]
|
83
|
+
else
|
84
|
+
part_name = image_contents[image_file_path].first
|
85
|
+
end
|
86
|
+
if images[image_file_path] > 1
|
87
|
+
if file_url
|
88
|
+
multipart << [
|
89
|
+
"\r\n--MHTML_IMAGES",
|
90
|
+
"\r\nContent-Location: ",
|
91
|
+
part_name,
|
92
|
+
"\r\nContent-Type: ",
|
93
|
+
mt,
|
94
|
+
"\r\nContent-Transfer-Encoding: base64\r\n\r\n",
|
95
|
+
encode64
|
96
|
+
]
|
97
|
+
"url(#{quot}mhtml:#{file_url}!#{part_name}#{quot})"
|
98
|
+
else
|
99
|
+
url_match
|
100
|
+
end
|
101
|
+
else
|
102
|
+
"url(#{quot}data:#{mt};base64,#{encode64}#{quot})"
|
103
|
+
end
|
104
|
+
else
|
105
|
+
url_match
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
if multipart.size > 1
|
110
|
+
content = content.gsub(/(\*?)(background-image:[^;\}]*?url\("?mhtml)/, '*\2')
|
111
|
+
(multipart + ["\r\n--MHTML_IMAGES--\r\n*/\r\n", content]).flatten.join("")
|
112
|
+
else
|
113
|
+
content
|
114
|
+
end
|
115
|
+
end
|
116
|
+
module_function :embed_images
|
117
|
+
|
118
|
+
def asset_url(file_path)
|
119
|
+
if defined?(::Rails) && file_path.start_with?(::Rails.public_path) && Cfg.mhtml_base_href?
|
120
|
+
URI.join(Cfg.mhtml_base_href, file_path[(::Rails.public_path.size)..-1])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
module_function :asset_url
|
124
|
+
|
125
|
+
def extract_images(content, file_path)
|
126
|
+
images = {}
|
127
|
+
content.scan(Cfg.url_matcher).flatten.each do |image_path|
|
128
|
+
image_file_path = asset_expand_path(image_path, file_path)
|
129
|
+
images[image_file_path] = (images[image_file_path] || 0) + 1
|
130
|
+
end
|
131
|
+
images
|
132
|
+
end
|
133
|
+
module_function :extract_images
|
134
|
+
|
135
|
+
def asset_expand_path(relative_asset_path, absolute_source_file_path)
|
136
|
+
File.expand_path(File.join("..", relative_asset_path), absolute_source_file_path)
|
137
|
+
end
|
138
|
+
module_function :asset_expand_path
|
139
|
+
|
140
|
+
def image_mime_type(path)
|
141
|
+
return if path !~ /\.([^\.]+)$/
|
142
|
+
case $1.downcase.to_sym
|
143
|
+
when :jpg, :jpeg
|
144
|
+
"image/jpeg"
|
145
|
+
when :png
|
146
|
+
"image/png"
|
147
|
+
when :gif
|
148
|
+
"image/gif"
|
149
|
+
when :tif, :tiff
|
150
|
+
"image/tiff"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
module_function :image_mime_type
|
154
|
+
|
155
|
+
def encoded_file_contents(file_path, css_file_path = nil)
|
156
|
+
if css_file_path && is_rake?
|
157
|
+
@image_report[file_path] ||= {:found_in => {}}
|
158
|
+
@image_report[file_path][:found_in][css_file_path] =
|
159
|
+
(@image_report[file_path][:found_in][css_file_path] || 0) + 1
|
160
|
+
if @image_report[file_path][:exists] = File.exists?(file_path)
|
161
|
+
@image_report[file_path][:embeddable] =
|
162
|
+
File.size(file_path) <= Cfg.maximum_embedded_file_size
|
163
|
+
end
|
164
|
+
end
|
165
|
+
if File.exists?(file_path) && File.size(file_path) <= Cfg.maximum_embedded_file_size
|
166
|
+
Base64.encode64(File.read(file_path)).gsub(/\n/, "")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
module_function :encoded_file_contents
|
170
|
+
|
171
|
+
def is_runtime?
|
172
|
+
! is_rake?
|
173
|
+
end
|
174
|
+
module_function :is_runtime?
|
175
|
+
|
176
|
+
def is_rake?
|
177
|
+
defined?(@image_report) && @image_report.is_a?(Hash)
|
178
|
+
end
|
179
|
+
module_function :is_rake?
|
180
|
+
|
181
|
+
def async_write_static_compressed_file(file_path, &block)
|
182
|
+
lock_file_path = "#{file_path}.locked"
|
183
|
+
return if File.exists?(lock_file_path) && File.mtime(lock_file_path) > (Time.now - 120)
|
184
|
+
File.open(lock_file_path, "w"){} # touch
|
185
|
+
Thread.new do
|
186
|
+
begin
|
187
|
+
file_path = write_static_compressed_file(file_path)
|
188
|
+
ensure
|
189
|
+
File.delete(lock_file_path)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
module_function :async_write_static_compressed_file
|
194
|
+
|
195
|
+
def write_static_compressed_file(file_path)
|
196
|
+
file_path, content = write_static_minified_asset(file_path)
|
197
|
+
file_path = write_static_gzipped_file(file_path, content)
|
198
|
+
end
|
199
|
+
module_function :write_static_compressed_file
|
200
|
+
|
201
|
+
def js_compressor
|
202
|
+
closure_compiler_js_compressor || yui_js_compressor
|
203
|
+
end
|
204
|
+
module_function :js_compressor
|
205
|
+
|
206
|
+
def css_compressor
|
207
|
+
yui_css_compressor
|
208
|
+
end
|
209
|
+
module_function :css_compressor
|
210
|
+
|
211
|
+
protected
|
212
|
+
|
213
|
+
begin
|
214
|
+
require "closure-compiler"
|
215
|
+
def closure_compiler_js_compressor
|
216
|
+
Closure::Compiler.new
|
217
|
+
end
|
218
|
+
rescue LoadError => e
|
219
|
+
STDERR.puts "WARN: #{e.message}"
|
220
|
+
def closure_compiler_js_compressor
|
221
|
+
end
|
222
|
+
end
|
223
|
+
module_function :closure_compiler_js_compressor
|
224
|
+
|
225
|
+
begin
|
226
|
+
require "yui/compressor"
|
227
|
+
def yui_js_compressor
|
228
|
+
YUI::JavaScriptCompressor.new
|
229
|
+
end
|
230
|
+
def yui_css_compressor
|
231
|
+
YUI::CssCompressor.new
|
232
|
+
end
|
233
|
+
rescue LoadError => e
|
234
|
+
STDERR.puts "WARN: #{e.message}"
|
235
|
+
def yui_js_compressor
|
236
|
+
end
|
237
|
+
def yui_css_compressor
|
238
|
+
end
|
239
|
+
end
|
240
|
+
module_function :yui_js_compressor
|
241
|
+
module_function :yui_css_compressor
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
module SlightAssets
|
2
|
+
version = nil
|
3
|
+
version = $1 if ::File.expand_path('../..', __FILE__) =~ /\/slight_assets-(\d[\w\.]+)/
|
4
|
+
version = ::StepUp::Driver::Git.new.last_version_tag if version.nil? && ::File.exists?(::File.expand_path('../../../.git', __FILE__))
|
5
|
+
VERSION = version.gsub(/^v?([^\+]+)\+?\d*$/, '\1')
|
6
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "slight_assets/base"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
slight_asset: &slight_asset
|
2
|
+
|
3
|
+
# Maximum embedded file size
|
4
|
+
# The limit file size to decide whether or not to embed
|
5
|
+
# Must be less than or equal to "32kB"
|
6
|
+
maximum_embedded_file_size: "32kB"
|
7
|
+
|
8
|
+
# Scope of assets to minify in batch process (rake).
|
9
|
+
# The prefix "-" indicates the files to exclude from minifying.
|
10
|
+
# Keep in mind that the sequence matters.
|
11
|
+
minify_assets:
|
12
|
+
- **/*.js
|
13
|
+
- **/*.css
|
14
|
+
# in the end, the masks below will be used for cleaning (even if omitted).
|
15
|
+
- -**/*.min.js
|
16
|
+
- -**/*.min.css
|
17
|
+
|
18
|
+
# Absolute MHTML base href (Optional)
|
19
|
+
# If present, the CSS images that was referenced twice or more
|
20
|
+
# will be embedded inside a MHTML block
|
21
|
+
#mhtml_base_href: "http://localhost:3000"
|
22
|
+
|
23
|
+
# JavaScript Reducer (Optional)
|
24
|
+
# This mode enables obfuscation for JavaScript compression, reducing
|
25
|
+
# even more the file size.
|
26
|
+
# Sets the JavaScript reduce mode:
|
27
|
+
# "rake" - Reduce only when executing rake task (asset:compress:js)
|
28
|
+
# true - Reduce at runtime or when executing rake
|
29
|
+
# false - Don't reduce JavaScript's
|
30
|
+
js_reducer: false
|
31
|
+
|
32
|
+
development:
|
33
|
+
slight_asset:
|
34
|
+
<<: *slight_asset
|
35
|
+
|
36
|
+
test:
|
37
|
+
slight_asset:
|
38
|
+
<<: *slight_asset
|
39
|
+
|
40
|
+
production:
|
41
|
+
slight_asset:
|
42
|
+
<<: *slight_asset
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slight_assets
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Marcelo Manzan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-20 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: yui-compressor
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 55
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 9
|
33
|
+
- 6
|
34
|
+
version: 0.9.6
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: step-up
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 6
|
49
|
+
- 0
|
50
|
+
version: 0.6.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: mocha
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: closure-compiler
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
type: :development
|
94
|
+
version_requirements: *id005
|
95
|
+
description: Optimize the assets of your Rails application without change any line of code.
|
96
|
+
email:
|
97
|
+
- manzan@gmail.com
|
98
|
+
executables: []
|
99
|
+
|
100
|
+
extensions: []
|
101
|
+
|
102
|
+
extra_rdoc_files: []
|
103
|
+
|
104
|
+
files:
|
105
|
+
- generators/slight_assets/slight_assets_generator.rb
|
106
|
+
- lib/slight_assets.rb
|
107
|
+
- lib/slight_assets/base.rb
|
108
|
+
- lib/slight_assets/js_reducer.rb
|
109
|
+
- lib/slight_assets/rails.rb
|
110
|
+
- lib/slight_assets/rake_tasks.rb
|
111
|
+
- lib/slight_assets/settings.rb
|
112
|
+
- lib/slight_assets/settings/default_config.yml
|
113
|
+
- lib/slight_assets/util.rb
|
114
|
+
- lib/slight_assets/version.rb
|
115
|
+
- templates/install/config/assets.yml
|
116
|
+
- templates/install/config/initializers/assets.rb
|
117
|
+
has_rdoc: true
|
118
|
+
homepage: https://github.com/kawamanza/slight_assets
|
119
|
+
licenses: []
|
120
|
+
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
none: false
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
hash: 23
|
141
|
+
segments:
|
142
|
+
- 1
|
143
|
+
- 3
|
144
|
+
- 6
|
145
|
+
version: 1.3.6
|
146
|
+
requirements: []
|
147
|
+
|
148
|
+
rubyforge_project: slight_assets
|
149
|
+
rubygems_version: 1.3.7
|
150
|
+
signing_key:
|
151
|
+
specification_version: 3
|
152
|
+
summary: All you need to make your rails application more fast.
|
153
|
+
test_files: []
|
154
|
+
|