juicer 0.2.6 → 1.0.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/History.txt +28 -0
- data/Rakefile +84 -36
- data/Readme.rdoc +192 -23
- data/VERSION +1 -0
- data/bin/juicer +2 -4
- data/lib/juicer.rb +9 -10
- data/lib/juicer/asset/path.rb +275 -0
- data/lib/juicer/asset/path_resolver.rb +79 -0
- data/lib/juicer/binary.rb +3 -5
- data/lib/juicer/cache_buster.rb +112 -27
- data/lib/juicer/command/install.rb +4 -2
- data/lib/juicer/command/list.rb +16 -9
- data/lib/juicer/command/merge.rb +30 -14
- data/lib/juicer/command/verify.rb +1 -1
- data/lib/juicer/css_cache_buster.rb +31 -47
- data/lib/juicer/datafy/datafy.rb +20 -0
- data/lib/juicer/dependency_resolver/css_dependency_resolver.rb +29 -0
- data/lib/juicer/dependency_resolver/dependency_resolver.rb +101 -0
- data/lib/juicer/dependency_resolver/javascript_dependency_resolver.rb +23 -0
- data/lib/juicer/ext/logger.rb +5 -0
- data/lib/juicer/ext/string.rb +47 -0
- data/lib/juicer/ext/symbol.rb +15 -0
- data/lib/juicer/image_embed.rb +129 -0
- data/lib/juicer/install/base.rb +2 -2
- data/lib/juicer/install/closure_compiler_installer.rb +69 -0
- data/lib/juicer/install/jslint_installer.rb +3 -3
- data/lib/juicer/install/rhino_installer.rb +3 -2
- data/lib/juicer/install/yui_compressor_installer.rb +3 -2
- data/lib/juicer/jslint.rb +1 -1
- data/lib/juicer/merger/base.rb +1 -1
- data/lib/juicer/merger/javascript_merger.rb +3 -4
- data/lib/juicer/merger/stylesheet_merger.rb +13 -15
- data/lib/juicer/minifyer/closure_compiler.rb +90 -0
- data/lib/juicer/minifyer/java_base.rb +77 -0
- data/lib/juicer/minifyer/yui_compressor.rb +15 -48
- data/test/bin/jslint-1.0.js +523 -0
- data/test/bin/jslint.js +523 -0
- data/test/bin/rhino1_7R1.zip +0 -0
- data/test/bin/rhino1_7R2-RC1.jar +0 -0
- data/test/bin/rhino1_7R2-RC1.zip +0 -0
- data/test/bin/yuicompressor +0 -0
- data/test/bin/yuicompressor-2.3.5.zip +0 -0
- data/test/bin/yuicompressor-2.4.2.jar +0 -0
- data/test/bin/yuicompressor-2.4.2.zip +0 -0
- data/test/data/Changelog.txt +10 -0
- data/test/data/a.css +3 -0
- data/test/data/a.js +5 -0
- data/test/data/a1.css +5 -0
- data/test/data/b.css +1 -0
- data/test/data/b.js +5 -0
- data/test/data/b1.css +5 -0
- data/test/data/c1.css +3 -0
- data/test/data/css/2.gif +1 -0
- data/test/data/css/test.css +11 -0
- data/test/data/css/test2.css +1 -0
- data/test/data/d1.css +3 -0
- data/test/data/images/1.png +1 -0
- data/test/data/my_app.js +2 -0
- data/test/data/not-ok.js +2 -0
- data/test/data/ok.js +3 -0
- data/test/data/path_test.css +5 -0
- data/test/data/path_test2.css +14 -0
- data/test/data/pkg/module/moda.js +2 -0
- data/test/data/pkg/module/modb.js +3 -0
- data/test/data/pkg/pkg.js +1 -0
- data/test/fixtures/yui-download.html +425 -0
- data/test/test_helper.rb +36 -7
- data/test/unit/juicer/asset/path_resolver_test.rb +76 -0
- data/test/unit/juicer/asset/path_test.rb +370 -0
- data/test/unit/juicer/cache_buster_test.rb +104 -0
- data/test/{juicer/test_chainable.rb → unit/juicer/chainable_test.rb} +1 -1
- data/test/unit/juicer/command/install_test.rb +58 -0
- data/test/{juicer/command/test_list.rb → unit/juicer/command/list_test.rb} +26 -14
- data/test/unit/juicer/command/merge_test.rb +162 -0
- data/test/{juicer/command/test_util.rb → unit/juicer/command/util_test.rb} +10 -6
- data/test/unit/juicer/command/verify_test.rb +48 -0
- data/test/{juicer/test_css_cache_buster.rb → unit/juicer/css_cache_buster_test.rb} +10 -30
- data/test/unit/juicer/datafy_test.rb +37 -0
- data/test/{juicer/merger/test_css_dependency_resolver.rb → unit/juicer/dependency_resolver/css_dependency_resolver_test.rb} +2 -2
- data/test/{juicer/merger/test_javascript_dependency_resolver.rb → unit/juicer/dependency_resolver/javascript_dependency_resolver_test.rb} +13 -2
- data/test/unit/juicer/ext/{#string_test.rb# → string_test.rb} +0 -7
- data/test/unit/juicer/ext/symbol_test.rb +27 -0
- data/test/unit/juicer/image_embed_test.rb +271 -0
- data/test/unit/juicer/install/installer_base_test.rb +214 -0
- data/test/{juicer/install/test_jslint_installer.rb → unit/juicer/install/jslint_installer_test.rb} +1 -1
- data/test/{juicer/install/test_rhino_installer.rb → unit/juicer/install/rhino_installer_test.rb} +1 -1
- data/test/{juicer/install/test_yui_compressor_installer.rb → unit/juicer/install/yui_compressor_test.rb} +16 -16
- data/test/unit/juicer/jslint_test.rb +60 -0
- data/test/{juicer/merger/test_base.rb → unit/juicer/merger/base_test.rb} +1 -1
- data/test/{juicer/merger/test_javascript_merger.rb → unit/juicer/merger/javascript_merger_test.rb} +2 -2
- data/test/{juicer/merger/test_stylesheet_merger.rb → unit/juicer/merger/stylesheet_merger_test.rb} +15 -13
- data/test/unit/juicer/minifyer/closure_compressor_test.rb +107 -0
- data/test/{integration → unit}/juicer/minifyer/yui_compressor_test.rb +30 -47
- data/test/unit/juicer_test.rb +1 -0
- metadata +207 -113
- data/lib/juicer/core.rb +0 -61
- data/lib/juicer/merger/css_dependency_resolver.rb +0 -25
- data/lib/juicer/merger/dependency_resolver.rb +0 -82
- data/lib/juicer/merger/javascript_dependency_resolver.rb +0 -21
- data/tasks/ann.rake +0 -80
- data/tasks/bones.rake +0 -20
- data/tasks/gem.rake +0 -201
- data/tasks/git.rake +0 -40
- data/tasks/notes.rake +0 -27
- data/tasks/post_load.rake +0 -34
- data/tasks/rdoc.rake +0 -51
- data/tasks/rubyforge.rake +0 -55
- data/tasks/setup.rb +0 -292
- data/tasks/spec.rake +0 -54
- data/tasks/svn.rake +0 -47
- data/tasks/test.rake +0 -40
- data/tasks/test/setup.rake +0 -35
- data/tasks/zentest.rake +0 -36
- data/test/juicer/command/test_install.rb +0 -53
- data/test/juicer/command/test_merge.rb +0 -160
- data/test/juicer/command/test_verify.rb +0 -33
- data/test/juicer/install/test_installer_base.rb +0 -195
- data/test/juicer/minifyer/test_yui_compressor.rb +0 -159
- data/test/juicer/test_cache_buster.rb +0 -58
- data/test/juicer/test_core.rb +0 -47
- data/test/juicer/test_jslint.rb +0 -33
- data/test/test_juicer.rb +0 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "juicer/command/util"
|
|
2
2
|
require "cmdparse"
|
|
3
3
|
require "pathname"
|
|
4
4
|
|
|
@@ -29,7 +29,9 @@ into Juicer installation directory, usually ~/.juicer
|
|
|
29
29
|
|
|
30
30
|
# Execute command
|
|
31
31
|
#
|
|
32
|
-
def execute(args)
|
|
32
|
+
def execute(*args)
|
|
33
|
+
args.flatten!
|
|
34
|
+
|
|
33
35
|
if args.length == 0
|
|
34
36
|
raise ArgumentError.new('Please provide a library to install')
|
|
35
37
|
end
|
data/lib/juicer/command/list.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "juicer/command/util"
|
|
2
2
|
require "cmdparse"
|
|
3
3
|
require "pathname"
|
|
4
4
|
|
|
@@ -12,9 +12,9 @@ module Juicer
|
|
|
12
12
|
|
|
13
13
|
# Initializes command
|
|
14
14
|
#
|
|
15
|
-
def initialize(
|
|
15
|
+
def initialize(log = nil)
|
|
16
16
|
super('list', false, true)
|
|
17
|
-
@
|
|
17
|
+
@log = log
|
|
18
18
|
self.short_desc = "Lists all dependencies for all input files/patterns"
|
|
19
19
|
self.description = <<-EOF
|
|
20
20
|
Dependencies are looked up recursively. The dependency chain reveals which files
|
|
@@ -34,16 +34,23 @@ Input parameters may be:
|
|
|
34
34
|
raise ArgumentError.new('Please provide atleast one input file/pattern')
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
types = { :js => Juicer::
|
|
38
|
-
:css => Juicer::
|
|
37
|
+
types = { :js => Juicer::JavaScriptDependencyResolver.new,
|
|
38
|
+
:css => Juicer::CssDependencyResolver.new }
|
|
39
39
|
|
|
40
|
-
files(args).
|
|
40
|
+
result = files(args).map { |file|
|
|
41
41
|
type = file.split(".").pop.to_sym
|
|
42
42
|
raise FileNotFoundError.new("Unable to guess type (CSS/JavaScript) of file #{relative(file)}") unless types[type]
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
deps = relative types[type].resolve(file)
|
|
45
|
+
# there may only be one dependency, which resolve() returns as a string
|
|
46
|
+
deps = deps.join("\n ") if deps.is_a? Array
|
|
47
|
+
|
|
48
|
+
"Dependency chain for #{relative file}:\n #{deps}"
|
|
49
|
+
}.join("\n\n") + "\n"
|
|
50
|
+
|
|
51
|
+
@log.info result
|
|
52
|
+
|
|
53
|
+
result
|
|
47
54
|
end
|
|
48
55
|
end
|
|
49
56
|
end
|
data/lib/juicer/command/merge.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "juicer/command/util"
|
|
2
|
+
require "juicer/command/verify"
|
|
3
3
|
require "cmdparse"
|
|
4
4
|
require "pathname"
|
|
5
5
|
|
|
@@ -25,11 +25,12 @@ module Juicer
|
|
|
25
25
|
@ignore = false # Ignore syntax problems if true
|
|
26
26
|
@cache_buster = :soft # What kind of cache buster to use, :soft or :hard
|
|
27
27
|
@hosts = nil # Hosts to use when replacing URLs in stylesheets
|
|
28
|
-
@
|
|
28
|
+
@document_root = nil # Used to understand absolute paths
|
|
29
29
|
@relative_urls = false # Make the merger use relative URLs
|
|
30
30
|
@absolute_urls = false # Make the merger use absolute URLs
|
|
31
|
-
@local_hosts = [] # Host names that are served from :
|
|
31
|
+
@local_hosts = [] # Host names that are served from :document_root
|
|
32
32
|
@verify = true # Verify js files with JsLint
|
|
33
|
+
@image_embed_type = :none # Embed images in css files, options are :none, :data_uri
|
|
33
34
|
|
|
34
35
|
@log = log || Logger.new(STDOUT)
|
|
35
36
|
|
|
@@ -47,16 +48,18 @@ inside the first multi-line comment. The switch is @depend or @depends, your
|
|
|
47
48
|
choice.
|
|
48
49
|
|
|
49
50
|
The -m --minifyer switch can be used to select which minifyer to use. Currently
|
|
50
|
-
only YUI Compressor is supported, ie -m yui_compressor (default). When using
|
|
51
|
-
the
|
|
51
|
+
only YUI Compressor and Google Closure Compiler is supported, ie -m yui_compressor (default) or -m closure_compiler. When using
|
|
52
|
+
the compressor the path should be the path to where the jar file is found.
|
|
52
53
|
EOF
|
|
53
54
|
|
|
54
55
|
self.options = CmdParse::OptionParserWrapper.new do |opt|
|
|
55
56
|
opt.on("-o", "--output file", "Output filename") { |filename| @output = filename }
|
|
56
57
|
opt.on("-p", "--path path", "Path to compressor binary") { |path| @opts[:bin_path] = path }
|
|
57
|
-
opt.on("-m", "--minifyer name", "Which minifer to use. Currently only supports yui_compressor") { |name| @minifyer = name }
|
|
58
|
+
opt.on("-m", "--minifyer name", "Which minifer to use. Currently only supports yui_compressor and closure compiler") { |name| @minifyer = name }
|
|
58
59
|
opt.on("-f", "--force", "Force overwrite of target file") { @force = true }
|
|
59
|
-
opt.on("-a", "--arguments arguments", "Arguments to minifyer, escape with quotes") { |arguments|
|
|
60
|
+
opt.on("-a", "--arguments arguments", "Arguments to minifyer, escape with quotes") { |arguments|
|
|
61
|
+
@arguments = arguments.to_s.gsub(/(^['"]|["']$)/, "")
|
|
62
|
+
}
|
|
60
63
|
opt.on("-i", "--ignore-problems", "Merge and minify even if verifyer finds problems") { @ignore = true }
|
|
61
64
|
opt.on("-s", "--skip-verification", "Skip JsLint verification (js files only). Not recomended!") { @verify = false }
|
|
62
65
|
opt.on("-t", "--type type", "Juicer can only guess type when files have .css or .js extensions. Specify js or\n" +
|
|
@@ -70,11 +73,15 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
70
73
|
(" " * 37) + "absolute URLs are used. Only valid for CSS files") { |t| @relative_urls = true }
|
|
71
74
|
opt.on("-b", "--absolute-urls", "Convert all referenced URLs to absolute URLs. Requires --document-root.\n" +
|
|
72
75
|
(" " * 37) + "Works with cycled asset hosts. Only valid for CSS files") { |t| @absolute_urls = true }
|
|
73
|
-
opt.on("-d", "--document-root dir", "Path to resolve absolute URLs relative to") { |path| @
|
|
76
|
+
opt.on("-d", "--document-root dir", "Path to resolve absolute URLs relative to") { |path| @document_root = path }
|
|
74
77
|
opt.on("-c", "--cache-buster type", "none, soft or hard. Default is soft, which adds timestamps to referenced\n" +
|
|
75
78
|
(" " * 37) + "URLs as query parameters. None leaves URLs untouched and hard alters file names") do |type|
|
|
76
79
|
@cache_buster = [:soft, :hard].include?(type.to_sym) ? type.to_sym : nil
|
|
77
80
|
end
|
|
81
|
+
opt.on("-e", "--embed-images type", "none or data_uri. Default is none. Data_uri embeds images using Base64 encoding\n" +
|
|
82
|
+
(" " * 37) + "None leaves URLs untouched. Candiate images must be flagged with '?embed=true to be considered") do |embed|
|
|
83
|
+
@image_embed_type = [:none, :data_uri].include?(embed.to_sym) ? embed.to_sym : nil
|
|
84
|
+
end
|
|
78
85
|
end
|
|
79
86
|
end
|
|
80
87
|
|
|
@@ -101,7 +108,7 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
101
108
|
# confused
|
|
102
109
|
merger = merger(output).new(files, :relative_urls => @relative_urls,
|
|
103
110
|
:absolute_urls => @absolute_urls,
|
|
104
|
-
:
|
|
111
|
+
:document_root => @document_root,
|
|
105
112
|
:hosts => @hosts)
|
|
106
113
|
|
|
107
114
|
# Fail if syntax trouble (js only)
|
|
@@ -112,7 +119,7 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
112
119
|
end
|
|
113
120
|
|
|
114
121
|
# Set command chain and execute
|
|
115
|
-
merger.set_next(cache_buster(output)).set_next(minifyer)
|
|
122
|
+
merger.set_next(image_embed(output)).set_next(cache_buster(output)).set_next(minifyer)
|
|
116
123
|
merger.save(output)
|
|
117
124
|
|
|
118
125
|
# Print report
|
|
@@ -120,7 +127,7 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
120
127
|
merger.files.each { |file| @log.info " #{relative file}" }
|
|
121
128
|
rescue FileNotFoundError => err
|
|
122
129
|
# Handle missing document-root option
|
|
123
|
-
puts err.message.sub(/:
|
|
130
|
+
puts err.message.sub(/:document_root/, "--document-root")
|
|
124
131
|
end
|
|
125
132
|
|
|
126
133
|
private
|
|
@@ -137,7 +144,8 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
137
144
|
@log.debug "Using #{@minifyer.camel_case} for minification"
|
|
138
145
|
|
|
139
146
|
return compressor
|
|
140
|
-
rescue NameError
|
|
147
|
+
rescue NameError => e
|
|
148
|
+
@log.fatal e.message
|
|
141
149
|
@log.fatal "No such minifyer '#{@minifyer}', aborting"
|
|
142
150
|
raise SystemExit.new("No such minifyer '#{@minifyer}', aborting")
|
|
143
151
|
rescue FileNotFoundError => e
|
|
@@ -172,9 +180,17 @@ the YUI Compressor the path should be the path to where the jar file is found.
|
|
|
172
180
|
#
|
|
173
181
|
def cache_buster(file)
|
|
174
182
|
return nil if !file || file !~ /\.css$/ || @cache_buster.nil?
|
|
175
|
-
Juicer::CssCacheBuster.new(:
|
|
183
|
+
Juicer::CssCacheBuster.new(:document_root => @document_root, :type => @cache_buster, :hosts => @local_hosts)
|
|
176
184
|
end
|
|
177
185
|
|
|
186
|
+
#
|
|
187
|
+
# Load image embed, only available for CSS files
|
|
188
|
+
#
|
|
189
|
+
def image_embed(file)
|
|
190
|
+
return nil if !file || file !~ /\.css$/ || @image_embed_type.nil?
|
|
191
|
+
Juicer::ImageEmbed.new( :type => @image_embed_type )
|
|
192
|
+
end
|
|
193
|
+
|
|
178
194
|
#
|
|
179
195
|
# Generate output file name. Optional argument is a filename to base the new
|
|
180
196
|
# name on. It will prepend the original suffix with ".min"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "juicer/chainable"
|
|
2
|
+
require "juicer/cache_buster"
|
|
3
|
+
require "juicer/asset/path_resolver"
|
|
3
4
|
|
|
4
5
|
module Juicer
|
|
5
6
|
#
|
|
@@ -12,23 +13,23 @@ module Juicer
|
|
|
12
13
|
# work.
|
|
13
14
|
#
|
|
14
15
|
# When dealing with CSS files that reference absolute URLs like /images/1.png
|
|
15
|
-
# you must specify the :
|
|
16
|
+
# you must specify the :document_root option that these URLs should be resolved
|
|
16
17
|
# against.
|
|
17
18
|
#
|
|
18
19
|
# When dealing with full URLs (ie including hosts) you can optionally specify
|
|
19
20
|
# an array of hosts to recognize as "local", meaning they serve assets from
|
|
20
|
-
# the :
|
|
21
|
+
# the :document_root directory. This way even asset host cycling can benefit from
|
|
21
22
|
# cache busters.
|
|
22
23
|
#
|
|
23
24
|
class CssCacheBuster
|
|
24
25
|
include Juicer::Chainable
|
|
25
26
|
|
|
26
27
|
def initialize(options = {})
|
|
27
|
-
@
|
|
28
|
-
@
|
|
28
|
+
@document_root = options[:document_root]
|
|
29
|
+
@document_root.sub!(%r{/?$}, "") if @document_root
|
|
29
30
|
@type = options[:type] || :soft
|
|
30
|
-
@hosts = (options[:hosts] || []).collect { |h| h.sub!(%r{/?$}, "") }
|
|
31
|
-
@contents = nil
|
|
31
|
+
@hosts = (options[:hosts] || []).collect { |h| h.sub!(%r{/?$}, "") }
|
|
32
|
+
@contents = @base = nil
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
#
|
|
@@ -36,20 +37,21 @@ module Juicer
|
|
|
36
37
|
#
|
|
37
38
|
def save(file, output = nil)
|
|
38
39
|
@contents = File.read(file)
|
|
40
|
+
self.base = File.dirname(file)
|
|
39
41
|
used = []
|
|
40
42
|
|
|
41
|
-
urls(file).each do |
|
|
43
|
+
urls(file).each do |asset|
|
|
42
44
|
begin
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if path != url
|
|
47
|
-
used << path
|
|
48
|
-
basename = File.basename(Juicer::CacheBuster.path(path, @type))
|
|
49
|
-
@contents.gsub!(url, File.join(File.dirname(url), basename))
|
|
50
|
-
end
|
|
45
|
+
next if used.include?(asset.path)
|
|
46
|
+
@contents.gsub!(asset.path, asset.path(:cache_buster_type => @type))
|
|
51
47
|
rescue Errno::ENOENT
|
|
52
|
-
puts "Unable to locate file #{path
|
|
48
|
+
puts "Unable to locate file #{asset.path}, skipping cache buster"
|
|
49
|
+
rescue ArgumentError => e
|
|
50
|
+
if e.message =~ /No document root/
|
|
51
|
+
raise FileNotFoundError.new("Unable to resolve path #{asset.path} without :document_root option")
|
|
52
|
+
else
|
|
53
|
+
raise e
|
|
54
|
+
end
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
|
|
@@ -67,40 +69,22 @@ module Juicer
|
|
|
67
69
|
@contents = File.read(file) unless @contents
|
|
68
70
|
|
|
69
71
|
@contents.scan(/url\([\s"']*([^\)"'\s]*)[\s"']*\)/m).collect do |match|
|
|
70
|
-
match.first
|
|
72
|
+
path_resolver.resolve(match.first)
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
# like a local one (ie so we can add cache buster)
|
|
80
|
-
catch(:continue) do
|
|
81
|
-
if target =~ %r{^[a-z]+\://}
|
|
82
|
-
# This could've been a one-liner, but I prefer to be
|
|
83
|
-
# able to read my own code ;)
|
|
84
|
-
@hosts.each do |host|
|
|
85
|
-
if target =~ /^#{host}/
|
|
86
|
-
target.sub!(/^#{host}/, "")
|
|
87
|
-
throw :continue
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# No known hosts matched, return
|
|
92
|
-
return target
|
|
93
|
-
end
|
|
94
|
-
end
|
|
76
|
+
protected
|
|
77
|
+
def base=(base)
|
|
78
|
+
@prev_base = @base
|
|
79
|
+
@base = base
|
|
80
|
+
end
|
|
95
81
|
|
|
96
|
-
|
|
97
|
-
if
|
|
98
|
-
raise FileNotFoundError.new("Unable to resolve absolute path #{target} without :web_root option") unless @web_root
|
|
99
|
-
return File.expand_path(File.join(@web_root, target))
|
|
100
|
-
end
|
|
82
|
+
def path_resolver
|
|
83
|
+
return @path_resolver if @path_resolver && @base == @prev_base
|
|
101
84
|
|
|
102
|
-
|
|
103
|
-
|
|
85
|
+
@path_resolver = Juicer::Asset::PathResolver.new(:document_root => @document_root,
|
|
86
|
+
:hosts => @hosts,
|
|
87
|
+
:base => @base)
|
|
104
88
|
end
|
|
105
89
|
end
|
|
106
90
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
|
|
3
|
+
# Datafy code lifted from http://segment7.net/projects/ruby/datafy/
|
|
4
|
+
|
|
5
|
+
require 'base64'
|
|
6
|
+
require 'cgi'
|
|
7
|
+
|
|
8
|
+
module Datafy
|
|
9
|
+
def Datafy::make_data_uri(content, content_type)
|
|
10
|
+
outuri = 'data:' + content_type
|
|
11
|
+
unless content_type =~ /^text/i # base64 encode if not text
|
|
12
|
+
outuri += ';base64'
|
|
13
|
+
content = Base64.encode64(content).gsub("\n", '')
|
|
14
|
+
else
|
|
15
|
+
content = CGI::escape(content)
|
|
16
|
+
end
|
|
17
|
+
outuri += ",#{content}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "juicer/dependency_resolver/dependency_resolver"
|
|
2
|
+
|
|
3
|
+
module Juicer
|
|
4
|
+
|
|
5
|
+
# Resolves @import statements in CSS files and builds a list of all
|
|
6
|
+
# files, in order.
|
|
7
|
+
#
|
|
8
|
+
class CssDependencyResolver < DependencyResolver
|
|
9
|
+
# Regexp borrowed from similar project:
|
|
10
|
+
# http://github.com/cgriego/front-end-blender/tree/master/lib/front_end_architect/blender.rb
|
|
11
|
+
@@import_pattern = /^\s*@import(?:\surl\(|\s)(['"]?)([^\?'"\)\s]+)(\?(?:[^'"\)]+)?)?\1\)?(?:[^?;]+)?;?/im
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def parse(line, imported_file = nil)
|
|
15
|
+
return $2 if line =~ @@import_pattern
|
|
16
|
+
|
|
17
|
+
# At first sight of actual CSS rules we abort (TODO: This does not take
|
|
18
|
+
# into account the fact that rules may be commented out and that more
|
|
19
|
+
# imports may follow)
|
|
20
|
+
throw :done if imported_file && line =~ %r{/*}
|
|
21
|
+
throw :done if line =~ /^[\.\#a-zA-Z\:]/
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def extension
|
|
25
|
+
".css"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
module Juicer
|
|
2
|
+
class DependencyResolver
|
|
3
|
+
include Enumerable
|
|
4
|
+
attr_reader :files
|
|
5
|
+
|
|
6
|
+
# Constructor
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
@files = []
|
|
9
|
+
@options = options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Resolve dependencies.
|
|
14
|
+
# This method accepts an optional block. The block will receive each
|
|
15
|
+
# file in succession. The file is included in the returned collection
|
|
16
|
+
# if the block is true for the given file. Without a block every found
|
|
17
|
+
# file is returned.
|
|
18
|
+
#
|
|
19
|
+
def resolve(file, &block)
|
|
20
|
+
@files = []
|
|
21
|
+
_resolve(file, &block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Yield files recursively. Resolve dependencies first, then call each, or
|
|
26
|
+
# any other enumerable methods.
|
|
27
|
+
#
|
|
28
|
+
def each(&block)
|
|
29
|
+
@files.each(&block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Resolves a path relative to another. If the path is absolute (ie it
|
|
34
|
+
# starts with a protocol or /) the <tt>:document_root</tt> options has to be
|
|
35
|
+
# set as well.
|
|
36
|
+
#
|
|
37
|
+
def resolve_path(path, reference)
|
|
38
|
+
# Absolute URL
|
|
39
|
+
if path =~ %r{^(/|[a-z]+:)}
|
|
40
|
+
if @options[:document_root].nil?
|
|
41
|
+
msg = "Cannot resolve absolute path '#{path}' without web root option"
|
|
42
|
+
raise ArgumentError.new(msg)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
path.sub!(%r{^[a-z]+://[^/]+/}, '')
|
|
46
|
+
return File.expand_path(File.join(@options[:document_root], path))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
File.expand_path(File.join(File.dirname(reference), path))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
def parse(line)
|
|
54
|
+
raise NotImplementedError.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def extension
|
|
58
|
+
raise NotImplementedError.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Carries out the actual work of resolve. resolve resets the internal
|
|
63
|
+
# file list and yields control to _resolve for rebuilding the file list.
|
|
64
|
+
#
|
|
65
|
+
def _resolve(file)
|
|
66
|
+
imported_path = nil
|
|
67
|
+
|
|
68
|
+
IO.foreach(file) do |line|
|
|
69
|
+
# Implementing subclasses may throw :done from the parse method when
|
|
70
|
+
# the file is exhausted for dependency declaration possibilities.
|
|
71
|
+
catch(:done) do
|
|
72
|
+
imported_path = parse(line, imported_path)
|
|
73
|
+
|
|
74
|
+
# If a dependency declaration was found
|
|
75
|
+
if imported_path
|
|
76
|
+
# Resolves a path relative to the file that imported it
|
|
77
|
+
imported_path = resolve_path(imported_path, file)
|
|
78
|
+
|
|
79
|
+
if File.directory?(imported_path)
|
|
80
|
+
imported_files = Dir.glob(File.join(imported_path, "**", "*#{extension}"))
|
|
81
|
+
else
|
|
82
|
+
imported_files = [imported_path]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
imported_files.each do |imported_file|
|
|
86
|
+
# Only keep processing file if it's not already included.
|
|
87
|
+
# Yield to block to allow caller to ignore file
|
|
88
|
+
if !@files.include?(imported_file) && (!block_given? || yield(imported_file))
|
|
89
|
+
# Check this file for imports before adding it to get order right
|
|
90
|
+
_resolve(imported_file) { |f| f != File.expand_path(file) }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
file = File.expand_path(file)
|
|
98
|
+
@files << file if !@files.include?(file) && (!block_given? || yield(file))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|