sprockets 1.0.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprockets might be problematic. Click here for more details.
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/lib/sprockets.rb +26 -37
- data/lib/sprockets/asset.rb +212 -0
- data/lib/sprockets/asset_attributes.rb +158 -0
- data/lib/sprockets/base.rb +163 -0
- data/lib/sprockets/bundled_asset.rb +258 -0
- data/lib/sprockets/cache/file_store.rb +41 -0
- data/lib/sprockets/caching.rb +123 -0
- data/lib/sprockets/charset_normalizer.rb +41 -0
- data/lib/sprockets/context.rb +217 -0
- data/lib/sprockets/digest.rb +67 -0
- data/lib/sprockets/directive_processor.rb +380 -0
- data/lib/sprockets/eco_template.rb +38 -0
- data/lib/sprockets/ejs_template.rb +37 -0
- data/lib/sprockets/engines.rb +98 -0
- data/lib/sprockets/environment.rb +81 -40
- data/lib/sprockets/errors.rb +17 -0
- data/lib/sprockets/index.rb +79 -0
- data/lib/sprockets/jst_processor.rb +26 -0
- data/lib/sprockets/mime.rb +38 -0
- data/lib/sprockets/processing.rb +280 -0
- data/lib/sprockets/processor.rb +32 -0
- data/lib/sprockets/safety_colons.rb +28 -0
- data/lib/sprockets/server.rb +272 -0
- data/lib/sprockets/static_asset.rb +86 -0
- data/lib/sprockets/trail.rb +114 -0
- data/lib/sprockets/utils.rb +67 -0
- data/lib/sprockets/version.rb +1 -7
- metadata +212 -64
- data/Rakefile +0 -19
- data/bin/sprocketize +0 -54
- data/ext/nph-sprockets.cgi +0 -127
- data/lib/sprockets/concatenation.rb +0 -36
- data/lib/sprockets/error.rb +0 -5
- data/lib/sprockets/pathname.rb +0 -37
- data/lib/sprockets/preprocessor.rb +0 -91
- data/lib/sprockets/secretary.rb +0 -106
- data/lib/sprockets/source_file.rb +0 -54
- data/lib/sprockets/source_line.rb +0 -82
- data/test/fixtures/assets/images/script_with_assets/one.png +0 -1
- data/test/fixtures/assets/images/script_with_assets/two.png +0 -1
- data/test/fixtures/assets/stylesheets/script_with_assets.css +0 -1
- data/test/fixtures/constants.yml +0 -1
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js +0 -8
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js +0 -2
- data/test/fixtures/multiline_comments_should_be_removed_by_default.js +0 -4
- data/test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js +0 -5
- data/test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js +0 -1
- data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +0 -3
- data/test/fixtures/requiring_the_current_file_should_do_nothing.js +0 -1
- data/test/fixtures/src/constants.yml +0 -3
- data/test/fixtures/src/foo.js +0 -1
- data/test/fixtures/src/foo/bar.js +0 -4
- data/test/fixtures/src/foo/foo.js +0 -1
- data/test/fixtures/src/script_with_assets.js +0 -3
- data/test/test_concatenation.rb +0 -28
- data/test/test_environment.rb +0 -64
- data/test/test_helper.rb +0 -55
- data/test/test_pathname.rb +0 -43
- data/test/test_preprocessor.rb +0 -107
- data/test/test_secretary.rb +0 -83
- data/test/test_source_file.rb +0 -34
- data/test/test_source_line.rb +0 -89
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# `AssetAttributes` is a wrapper similar to `Pathname` that provides
|
5
|
+
# some helper accessors.
|
6
|
+
#
|
7
|
+
# These methods should be considered internalish.
|
8
|
+
class AssetAttributes
|
9
|
+
attr_reader :environment, :pathname
|
10
|
+
|
11
|
+
def initialize(environment, path)
|
12
|
+
@environment = environment
|
13
|
+
@pathname = path.is_a?(Pathname) ? path : Pathname.new(path.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Replaces `$root` placeholder with actual environment root.
|
17
|
+
def expand_root
|
18
|
+
pathname.to_s.sub(/^\$root/, environment.root)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Replaces environment root with `$root` placeholder.
|
22
|
+
def relativize_root
|
23
|
+
pathname.to_s.sub(/^#{Regexp.escape(environment.root)}/, '$root')
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns paths search the load path for.
|
27
|
+
def search_paths
|
28
|
+
paths = [pathname.to_s]
|
29
|
+
|
30
|
+
if pathname.basename(extensions.join).to_s != 'index'
|
31
|
+
path_without_extensions = extensions.inject(pathname) { |p, ext| p.sub(ext, '') }
|
32
|
+
index_path = path_without_extensions.join("index#{extensions.join}").to_s
|
33
|
+
paths << index_path
|
34
|
+
end
|
35
|
+
|
36
|
+
paths
|
37
|
+
end
|
38
|
+
|
39
|
+
# Reverse guess logical path for fully expanded path.
|
40
|
+
#
|
41
|
+
# This has some known issues. For an example if a file is
|
42
|
+
# shaddowed in the path, but is required relatively, its logical
|
43
|
+
# path will be incorrect.
|
44
|
+
def logical_path
|
45
|
+
raise ArgumentError unless pathname.absolute?
|
46
|
+
|
47
|
+
if root_path = environment.paths.detect { |path| pathname.to_s[path] }
|
48
|
+
path = pathname.relative_path_from(Pathname.new(root_path)).to_s
|
49
|
+
path = engine_extensions.inject(path) { |p, ext| p.sub(ext, '') }
|
50
|
+
path = "#{path}#{engine_format_extension}" unless format_extension
|
51
|
+
path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns `Array` of extension `String`s.
|
56
|
+
#
|
57
|
+
# "foo.js.coffee"
|
58
|
+
# # => [".js", ".coffee"]
|
59
|
+
#
|
60
|
+
def extensions
|
61
|
+
@extensions ||= @pathname.basename.to_s.scan(/\.[^.]+/)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the format extension.
|
65
|
+
#
|
66
|
+
# "foo.js.coffee"
|
67
|
+
# # => ".js"
|
68
|
+
#
|
69
|
+
def format_extension
|
70
|
+
extensions.detect { |ext|
|
71
|
+
@environment.mime_types(ext) && !@environment.engines(ext)
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns an `Array` of engine extensions.
|
76
|
+
#
|
77
|
+
# "foo.js.coffee.erb"
|
78
|
+
# # => [".coffee", ".erb"]
|
79
|
+
#
|
80
|
+
def engine_extensions
|
81
|
+
exts = extensions
|
82
|
+
|
83
|
+
if offset = extensions.index(format_extension)
|
84
|
+
exts = extensions[offset+1..-1]
|
85
|
+
end
|
86
|
+
|
87
|
+
exts.select { |ext| @environment.engines(ext) }
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns engine classes.
|
91
|
+
def engines
|
92
|
+
engine_extensions.map { |ext| @environment.engines(ext) }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns all processors to run on the path.
|
96
|
+
def processors
|
97
|
+
environment.preprocessors(content_type) +
|
98
|
+
engines.reverse +
|
99
|
+
environment.postprocessors(content_type)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the content type for the pathname. Falls back to `application/octet-stream`.
|
103
|
+
def content_type
|
104
|
+
@content_type ||= begin
|
105
|
+
if format_extension.nil?
|
106
|
+
engine_content_type || 'application/octet-stream'
|
107
|
+
else
|
108
|
+
@environment.mime_types(format_extension) ||
|
109
|
+
engine_content_type ||
|
110
|
+
'application/octet-stream'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Gets digest fingerprint.
|
116
|
+
#
|
117
|
+
# "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
|
118
|
+
# # => "0aa2105d29558f3eb790d411d7d8fb66"
|
119
|
+
#
|
120
|
+
def path_fingerprint
|
121
|
+
pathname.basename(extensions.join).to_s =~ /-([0-9a-f]{7,40})$/ ? $1 : nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Injects digest fingerprint into path.
|
125
|
+
#
|
126
|
+
# "foo.js"
|
127
|
+
# # => "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
|
128
|
+
#
|
129
|
+
def path_with_fingerprint(digest)
|
130
|
+
if old_digest = path_fingerprint
|
131
|
+
pathname.sub(old_digest, digest).to_s
|
132
|
+
else
|
133
|
+
basename = "#{pathname.basename(extensions.join)}-#{digest}#{extensions.join}"
|
134
|
+
pathname.dirname.to_s == '.' ? basename : pathname.dirname.join(basename).to_s
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
# Returns implicit engine content type.
|
140
|
+
#
|
141
|
+
# `.coffee` files carry an implicit `application/javascript`
|
142
|
+
# content type.
|
143
|
+
def engine_content_type
|
144
|
+
engines.reverse.each do |engine|
|
145
|
+
if engine.respond_to?(:default_mime_type) && engine.default_mime_type
|
146
|
+
return engine.default_mime_type
|
147
|
+
end
|
148
|
+
end
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def engine_format_extension
|
153
|
+
if content_type = engine_content_type
|
154
|
+
environment.extension_for_mime_type(content_type)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'sprockets/asset_attributes'
|
2
|
+
require 'sprockets/bundled_asset'
|
3
|
+
require 'sprockets/caching'
|
4
|
+
require 'sprockets/digest'
|
5
|
+
require 'sprockets/processing'
|
6
|
+
require 'sprockets/server'
|
7
|
+
require 'sprockets/static_asset'
|
8
|
+
require 'sprockets/trail'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
module Sprockets
|
12
|
+
# `Base` class for `Environment` and `Index`.
|
13
|
+
class Base
|
14
|
+
include Digest
|
15
|
+
include Caching, Processing, Server, Trail
|
16
|
+
|
17
|
+
# Get and set `Logger` instance.
|
18
|
+
attr_accessor :logger
|
19
|
+
|
20
|
+
# Get `Context` class.
|
21
|
+
#
|
22
|
+
# This class maybe mutated and mixed in with custom helpers.
|
23
|
+
#
|
24
|
+
# environment.context_class.instance_eval do
|
25
|
+
# include MyHelpers
|
26
|
+
# def asset_url; end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
attr_reader :context_class
|
30
|
+
|
31
|
+
# Get persistent cache store
|
32
|
+
attr_reader :cache
|
33
|
+
|
34
|
+
# Set persistent cache store
|
35
|
+
#
|
36
|
+
# The cache store must implement a pair of getters and
|
37
|
+
# setters. Either `get(key)`/`set(key, value)`,
|
38
|
+
# `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
|
39
|
+
def cache=(cache)
|
40
|
+
expire_index!
|
41
|
+
@cache = cache
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return an `Index`. Must be implemented by the subclass.
|
45
|
+
def index
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Works like `Dir.entries`.
|
50
|
+
#
|
51
|
+
# Subclasses may cache this method.
|
52
|
+
def entries(pathname)
|
53
|
+
trail.entries(pathname)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Works like `File.stat`.
|
57
|
+
#
|
58
|
+
# Subclasses may cache this method.
|
59
|
+
def stat(path)
|
60
|
+
trail.stat(path)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Read and compute digest of filename.
|
64
|
+
#
|
65
|
+
# Subclasses may cache this method.
|
66
|
+
def file_digest(path, data = nil)
|
67
|
+
if stat = self.stat(path)
|
68
|
+
# `data` maybe provided
|
69
|
+
if data
|
70
|
+
digest.update(data)
|
71
|
+
|
72
|
+
# If its a file, digest the contents
|
73
|
+
elsif stat.file?
|
74
|
+
digest.file(path.to_s)
|
75
|
+
|
76
|
+
# If its a directive, digest the list of filenames
|
77
|
+
elsif stat.directory?
|
78
|
+
contents = self.entries(path).join(',')
|
79
|
+
digest.update(contents)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Internal. Return a `AssetAttributes` for `path`.
|
85
|
+
def attributes_for(path)
|
86
|
+
AssetAttributes.new(self, path)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Internal. Return content type of `path`.
|
90
|
+
def content_type_of(path)
|
91
|
+
attributes_for(path).content_type
|
92
|
+
end
|
93
|
+
|
94
|
+
# Find asset by logical path or expanded path.
|
95
|
+
def find_asset(path, options = {})
|
96
|
+
pathname = Pathname.new(path)
|
97
|
+
|
98
|
+
if pathname.absolute?
|
99
|
+
build_asset(attributes_for(pathname).logical_path, pathname, options)
|
100
|
+
else
|
101
|
+
find_asset_in_path(pathname, options)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Preferred `find_asset` shorthand.
|
106
|
+
#
|
107
|
+
# environment['application.js']
|
108
|
+
#
|
109
|
+
def [](*args)
|
110
|
+
find_asset(*args)
|
111
|
+
end
|
112
|
+
|
113
|
+
def each_file
|
114
|
+
return to_enum(__method__) unless block_given?
|
115
|
+
paths.each do |base_path|
|
116
|
+
Dir["#{base_path}/**/*"].each do |filename|
|
117
|
+
yield filename unless File.directory?(filename)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def each_logical_path
|
124
|
+
return to_enum(__method__) unless block_given?
|
125
|
+
files = {}
|
126
|
+
each_file do |filename|
|
127
|
+
logical_path = attributes_for(filename).logical_path
|
128
|
+
yield logical_path unless files[logical_path]
|
129
|
+
files[logical_path] = true
|
130
|
+
end
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
# Pretty inspect
|
135
|
+
def inspect
|
136
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} " +
|
137
|
+
"root=#{root.to_s.inspect}, " +
|
138
|
+
"paths=#{paths.inspect}, " +
|
139
|
+
"digest=#{digest.to_s.inspect}" +
|
140
|
+
">"
|
141
|
+
end
|
142
|
+
|
143
|
+
protected
|
144
|
+
# Clear index after mutating state. Must be implemented by the subclass.
|
145
|
+
def expire_index!
|
146
|
+
raise NotImplementedError
|
147
|
+
end
|
148
|
+
|
149
|
+
def build_asset(logical_path, pathname, options)
|
150
|
+
pathname = Pathname.new(pathname)
|
151
|
+
|
152
|
+
return unless stat(pathname)
|
153
|
+
|
154
|
+
# If there are any processors to run on the pathname, use
|
155
|
+
# `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
|
156
|
+
if attributes_for(pathname).processors.any?
|
157
|
+
BundledAsset.new(self, logical_path, pathname, options)
|
158
|
+
else
|
159
|
+
StaticAsset.new(self, logical_path, pathname)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'sprockets/asset'
|
2
|
+
require 'sprockets/errors'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'set'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
module Sprockets
|
8
|
+
# `BundledAsset`s are used for files that need to be processed and
|
9
|
+
# concatenated with other assets. Use for `.js` and `.css` files.
|
10
|
+
class BundledAsset < Asset
|
11
|
+
# Define extra attributes to be serialized.
|
12
|
+
def self.serialized_attributes
|
13
|
+
super + %w( content_type mtime )
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(environment, logical_path, pathname, options)
|
17
|
+
super(environment, logical_path, pathname)
|
18
|
+
@options = options || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Initialize `BundledAsset` from serialized `Hash`.
|
22
|
+
def init_with(environment, coder)
|
23
|
+
@options = {}
|
24
|
+
|
25
|
+
super
|
26
|
+
|
27
|
+
@body = coder['body']
|
28
|
+
@assets = coder['asset_paths'].map { |p|
|
29
|
+
p = expand_root_path(p)
|
30
|
+
p == pathname.to_s ? self : environment[p, @options]
|
31
|
+
}
|
32
|
+
|
33
|
+
@dependency_paths = coder['dependency_paths'].map { |h|
|
34
|
+
h.merge('path' => expand_root_path(h['path']))
|
35
|
+
}
|
36
|
+
@dependency_paths.each do |dep|
|
37
|
+
dep['mtime'] = Time.parse(dep['mtime']) if dep['mtime'].is_a?(String)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Serialize custom attributes in `BundledAsset`.
|
42
|
+
def encode_with(coder)
|
43
|
+
super
|
44
|
+
|
45
|
+
coder['body'] = body
|
46
|
+
coder['asset_paths'] = to_a.map { |a| relativize_root_path(a.pathname) }
|
47
|
+
coder['dependency_paths'] = dependency_paths.map { |h|
|
48
|
+
h.merge('path' => relativize_root_path(h['path']))
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get asset's own processed contents. Excludes any of its required
|
53
|
+
# dependencies but does run any processors or engines on the
|
54
|
+
# original file.
|
55
|
+
def body
|
56
|
+
@body ||= build_dependency_context_and_body[1]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get latest mtime of all its dependencies.
|
60
|
+
def mtime
|
61
|
+
@mtime ||= dependency_paths.map { |h| h['mtime'] }.max
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get size of concatenated source.
|
65
|
+
def length
|
66
|
+
@length ||= build_source['length']
|
67
|
+
end
|
68
|
+
|
69
|
+
# Compute digest of concatenated source.
|
70
|
+
def digest
|
71
|
+
@digest ||= build_source['digest']
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return an `Array` of `Asset` files that are declared dependencies.
|
75
|
+
def dependencies
|
76
|
+
to_a - [self]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Expand asset into an `Array` of parts.
|
80
|
+
def to_a
|
81
|
+
@assets ||= build_dependencies_paths_and_assets[1]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Checks if Asset is stale by comparing the actual mtime and
|
85
|
+
# digest to the inmemory model.
|
86
|
+
def fresh?
|
87
|
+
# Check freshness of all declared dependencies
|
88
|
+
dependency_paths.all? { |h| dependency_fresh?(h) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return `String` of concatenated source.
|
92
|
+
def to_s
|
93
|
+
@source ||= build_source['source']
|
94
|
+
end
|
95
|
+
|
96
|
+
# Save asset to disk.
|
97
|
+
def write_to(filename, options = {})
|
98
|
+
# Gzip contents if filename has '.gz'
|
99
|
+
options[:compress] ||= File.extname(filename) == '.gz'
|
100
|
+
|
101
|
+
File.open("#{filename}+", 'wb') do |f|
|
102
|
+
if options[:compress]
|
103
|
+
# Run contents through `Zlib`
|
104
|
+
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
|
105
|
+
gz.write to_s
|
106
|
+
gz.close
|
107
|
+
else
|
108
|
+
# Write out as is
|
109
|
+
f.write to_s
|
110
|
+
f.close
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Atomic write
|
115
|
+
FileUtils.mv("#{filename}+", filename)
|
116
|
+
|
117
|
+
# Set mtime correctly
|
118
|
+
File.utime(mtime, mtime, filename)
|
119
|
+
|
120
|
+
nil
|
121
|
+
ensure
|
122
|
+
# Ensure tmp file gets cleaned up
|
123
|
+
FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
# Return new blank `Context` to evaluate processors in.
|
128
|
+
def blank_context
|
129
|
+
environment.context_class.new(environment, logical_path.to_s, pathname)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get `Context` after processors have been ran on it. This
|
133
|
+
# trackes any dependencies that processors have added to it.
|
134
|
+
def dependency_context
|
135
|
+
@dependency_context ||= build_dependency_context_and_body[0]
|
136
|
+
end
|
137
|
+
|
138
|
+
# All paths that this asset depends on. This list may include
|
139
|
+
# non-assets like directories.
|
140
|
+
def dependency_paths
|
141
|
+
@dependency_paths ||= build_dependencies_paths_and_assets[0]
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def logger
|
146
|
+
environment.logger
|
147
|
+
end
|
148
|
+
|
149
|
+
# Check if self has already been required and raise a fast
|
150
|
+
# error. Otherwise you end up with a StackOverflow error.
|
151
|
+
def check_circular_dependency!
|
152
|
+
requires = @options[:_requires] ||= []
|
153
|
+
if requires.include?(pathname.to_s)
|
154
|
+
raise CircularDependencyError, "#{pathname} has already been required"
|
155
|
+
end
|
156
|
+
requires << pathname.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
def build_dependency_context_and_body
|
160
|
+
start_time = Time.now.to_f
|
161
|
+
|
162
|
+
context = blank_context
|
163
|
+
|
164
|
+
# Read original data once and pass it along to `Context`
|
165
|
+
data = Sprockets::Utils.read_unicode(pathname)
|
166
|
+
|
167
|
+
# Prime digest cache with data, since we happen to have it
|
168
|
+
environment.file_digest(pathname, data)
|
169
|
+
|
170
|
+
# Runs all processors on `Context`
|
171
|
+
body = context.evaluate(pathname, :data => data)
|
172
|
+
|
173
|
+
@dependency_context, @body = context, body
|
174
|
+
|
175
|
+
elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
|
176
|
+
logger.info "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
|
177
|
+
|
178
|
+
return context, body
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_dependencies_paths_and_assets
|
182
|
+
check_circular_dependency!
|
183
|
+
|
184
|
+
paths, assets = {}, []
|
185
|
+
|
186
|
+
# Define an `add_dependency` helper
|
187
|
+
add_dependency = lambda do |asset|
|
188
|
+
unless assets.any? { |a| a.pathname == asset.pathname }
|
189
|
+
assets << asset
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Iterate over all the declared require paths from the `Context`
|
194
|
+
dependency_context._required_paths.each do |required_path|
|
195
|
+
# Catch `require_self`
|
196
|
+
if required_path == pathname.to_s
|
197
|
+
add_dependency.call(self)
|
198
|
+
else
|
199
|
+
# Recursively lookup required asset
|
200
|
+
environment[required_path, @options].to_a.each do |asset|
|
201
|
+
add_dependency.call(asset)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Ensure self is added to the dependency list
|
207
|
+
add_dependency.call(self)
|
208
|
+
|
209
|
+
dependency_context._dependency_paths.each do |path|
|
210
|
+
paths[path] ||= {
|
211
|
+
'path' => path,
|
212
|
+
'mtime' => environment.stat(path).mtime,
|
213
|
+
'hexdigest' => environment.file_digest(path).hexdigest
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
dependency_context._dependency_assets.each do |path|
|
218
|
+
# Skip if depending on self
|
219
|
+
next if path == pathname.to_s
|
220
|
+
|
221
|
+
# Recursively lookup required asset
|
222
|
+
environment[path, @options].to_a.each do |asset|
|
223
|
+
asset.dependency_paths.each do |dep|
|
224
|
+
paths[dep['path']] ||= dep
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
@dependency_paths, @assets = paths.values, assets
|
230
|
+
|
231
|
+
return @dependency_paths, @assets
|
232
|
+
end
|
233
|
+
|
234
|
+
def build_source
|
235
|
+
hash = environment.cache_hash("#{pathname}:source", id) do
|
236
|
+
data = ""
|
237
|
+
|
238
|
+
# Explode Asset into parts and gather the dependency bodies
|
239
|
+
to_a.each { |dependency| data << dependency.body }
|
240
|
+
|
241
|
+
# Run bundle processors on concatenated source
|
242
|
+
data = blank_context.evaluate(pathname, :data => data,
|
243
|
+
:processors => environment.bundle_processors(content_type))
|
244
|
+
|
245
|
+
{ 'length' => Rack::Utils.bytesize(data),
|
246
|
+
'digest' => environment.digest.update(data).hexdigest,
|
247
|
+
'source' => data }
|
248
|
+
end
|
249
|
+
hash['length'] = Integer(hash['length']) if hash['length'].is_a?(String)
|
250
|
+
|
251
|
+
@length = hash['length']
|
252
|
+
@digest = hash['digest']
|
253
|
+
@source = hash['source']
|
254
|
+
|
255
|
+
hash
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|