sprockets 3.0.1 → 3.7.5
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +308 -0
- data/README.md +49 -187
- data/bin/sprockets +1 -0
- data/lib/sprockets/asset.rb +3 -2
- data/lib/sprockets/base.rb +13 -2
- data/lib/sprockets/bundle.rb +5 -1
- data/lib/sprockets/cache/file_store.rb +7 -4
- data/lib/sprockets/cache.rb +6 -4
- data/lib/sprockets/closure_compressor.rb +5 -11
- data/lib/sprockets/coffee_script_processor.rb +2 -2
- data/lib/sprockets/coffee_script_template.rb +12 -1
- data/lib/sprockets/compressing.rb +20 -0
- data/lib/sprockets/dependencies.rb +8 -8
- data/lib/sprockets/deprecation.rb +90 -0
- data/lib/sprockets/digest_utils.rb +81 -57
- data/lib/sprockets/directive_processor.rb +2 -0
- data/lib/sprockets/eco_processor.rb +2 -2
- data/lib/sprockets/eco_template.rb +12 -1
- data/lib/sprockets/ejs_processor.rb +2 -2
- data/lib/sprockets/ejs_template.rb +12 -1
- data/lib/sprockets/encoding_utils.rb +7 -4
- data/lib/sprockets/engines.rb +11 -0
- data/lib/sprockets/erb_processor.rb +13 -1
- data/lib/sprockets/erb_template.rb +6 -1
- data/lib/sprockets/errors.rb +0 -1
- data/lib/sprockets/http_utils.rb +3 -1
- data/lib/sprockets/legacy.rb +20 -12
- data/lib/sprockets/legacy_proc_processor.rb +1 -1
- data/lib/sprockets/legacy_tilt_processor.rb +2 -2
- data/lib/sprockets/loader.rb +208 -59
- data/lib/sprockets/manifest.rb +57 -6
- data/lib/sprockets/mime.rb +26 -6
- data/lib/sprockets/path_utils.rb +20 -15
- data/lib/sprockets/processing.rb +10 -0
- data/lib/sprockets/processor_utils.rb +77 -0
- data/lib/sprockets/resolve.rb +10 -7
- data/lib/sprockets/sass_cache_store.rb +6 -1
- data/lib/sprockets/sass_compressor.rb +9 -17
- data/lib/sprockets/sass_processor.rb +16 -9
- data/lib/sprockets/sass_template.rb +14 -2
- data/lib/sprockets/server.rb +34 -14
- data/lib/sprockets/uglifier_compressor.rb +6 -13
- data/lib/sprockets/unloaded_asset.rb +137 -0
- data/lib/sprockets/uri_tar.rb +98 -0
- data/lib/sprockets/uri_utils.rb +14 -11
- data/lib/sprockets/utils/gzip.rb +67 -0
- data/lib/sprockets/utils.rb +36 -18
- data/lib/sprockets/version.rb +1 -1
- data/lib/sprockets/yui_compressor.rb +4 -14
- data/lib/sprockets.rb +21 -11
- metadata +49 -11
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Sprockets
|
2
4
|
# Functional utilities for dealing with Processor functions.
|
3
5
|
#
|
@@ -99,5 +101,80 @@ module Sprockets
|
|
99
101
|
def processors_cache_keys(processors)
|
100
102
|
processors.map { |processor| processor_cache_key(processor) }
|
101
103
|
end
|
104
|
+
|
105
|
+
# Internal: Set of all "simple" value types allowed to be returned in
|
106
|
+
# processor metadata.
|
107
|
+
VALID_METADATA_VALUE_TYPES = Set.new([
|
108
|
+
String,
|
109
|
+
Symbol,
|
110
|
+
TrueClass,
|
111
|
+
FalseClass,
|
112
|
+
NilClass
|
113
|
+
] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
|
114
|
+
|
115
|
+
# Internal: Set of all nested compound metadata types that can nest values.
|
116
|
+
VALID_METADATA_COMPOUND_TYPES = Set.new([
|
117
|
+
Array,
|
118
|
+
Hash,
|
119
|
+
Set
|
120
|
+
]).freeze
|
121
|
+
|
122
|
+
# Internal: Hash of all "simple" value types allowed to be returned in
|
123
|
+
# processor metadata.
|
124
|
+
VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash|
|
125
|
+
hash[type] = true
|
126
|
+
end.freeze
|
127
|
+
|
128
|
+
# Internal: Hash of all nested compound metadata types that can nest values.
|
129
|
+
VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash|
|
130
|
+
hash[type] = true
|
131
|
+
end.freeze
|
132
|
+
|
133
|
+
# Internal: Set of all allowed metadata types.
|
134
|
+
VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze
|
135
|
+
|
136
|
+
# Internal: Validate returned result of calling a processor pipeline and
|
137
|
+
# raise a friendly user error message.
|
138
|
+
#
|
139
|
+
# result - Metadata Hash returned from call_processors
|
140
|
+
#
|
141
|
+
# Returns result or raises a TypeError.
|
142
|
+
def validate_processor_result!(result)
|
143
|
+
if !result.instance_of?(Hash)
|
144
|
+
raise TypeError, "processor metadata result was expected to be a Hash, but was #{result.class}"
|
145
|
+
end
|
146
|
+
|
147
|
+
if !result[:data].instance_of?(String)
|
148
|
+
raise TypeError, "processor :data was expected to be a String, but as #{result[:data].class}"
|
149
|
+
end
|
150
|
+
|
151
|
+
result.each do |key, value|
|
152
|
+
if !key.instance_of?(Symbol)
|
153
|
+
raise TypeError, "processor metadata[#{key.inspect}] expected to be a Symbol"
|
154
|
+
end
|
155
|
+
|
156
|
+
if !valid_processor_metadata_value?(value)
|
157
|
+
raise TypeError, "processor metadata[:#{key}] returned a complex type: #{value.inspect}\n" +
|
158
|
+
"Only #{VALID_METADATA_TYPES.to_a.join(", ")} maybe used."
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
# Internal: Validate object is in validate metadata whitelist.
|
166
|
+
#
|
167
|
+
# value - Any Object
|
168
|
+
#
|
169
|
+
# Returns true if class is in whitelist otherwise false.
|
170
|
+
def valid_processor_metadata_value?(value)
|
171
|
+
if VALID_METADATA_VALUE_TYPES_HASH[value.class]
|
172
|
+
true
|
173
|
+
elsif VALID_METADATA_COMPOUND_TYPES_HASH[value.class]
|
174
|
+
value.all? { |v| valid_processor_metadata_value?(v) }
|
175
|
+
else
|
176
|
+
false
|
177
|
+
end
|
178
|
+
end
|
102
179
|
end
|
103
180
|
end
|
data/lib/sprockets/resolve.rb
CHANGED
@@ -53,7 +53,15 @@ module Sprockets
|
|
53
53
|
|
54
54
|
unless uri
|
55
55
|
message = "couldn't find file '#{path}'"
|
56
|
+
|
57
|
+
if relative_path?(path) && options[:base_path]
|
58
|
+
load_path, _ = paths_split(config[:paths], options[:base_path])
|
59
|
+
message << " under '#{load_path}'"
|
60
|
+
end
|
61
|
+
|
56
62
|
message << " with type '#{options[:accept]}'" if options[:accept]
|
63
|
+
message << "\nChecked in these paths: \n #{ config[:paths].join("\n ") }"
|
64
|
+
|
57
65
|
raise FileNotFound, message
|
58
66
|
end
|
59
67
|
|
@@ -144,12 +152,6 @@ module Sprockets
|
|
144
152
|
accepts
|
145
153
|
end
|
146
154
|
|
147
|
-
def normalize_logical_path(path)
|
148
|
-
dirname, basename = File.split(path)
|
149
|
-
path = dirname if basename == 'index'
|
150
|
-
path
|
151
|
-
end
|
152
|
-
|
153
155
|
def path_matches(load_path, logical_name, logical_basename)
|
154
156
|
dirname = File.dirname(File.join(load_path, logical_name))
|
155
157
|
candidates = dirname_matches(dirname, logical_basename)
|
@@ -176,6 +178,7 @@ module Sprockets
|
|
176
178
|
candidates = []
|
177
179
|
entries = self.entries(dirname)
|
178
180
|
entries.each do |entry|
|
181
|
+
next unless File.basename(entry).start_with?(basename)
|
179
182
|
name, type, _, _ = parse_path_extnames(entry)
|
180
183
|
if basename == name
|
181
184
|
candidates << [File.join(dirname, entry), type]
|
@@ -199,7 +202,7 @@ module Sprockets
|
|
199
202
|
|
200
203
|
if extname
|
201
204
|
path = path.chomp(extname)
|
202
|
-
type, engines, pipeline = value
|
205
|
+
type, engines, pipeline = value
|
203
206
|
end
|
204
207
|
|
205
208
|
return path, type, engines, pipeline
|
@@ -25,5 +25,10 @@ module Sprockets
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# Deprecated: Use Sprockets::SassProcessor::CacheStore instead.
|
28
|
-
SassCacheStore
|
28
|
+
class SassCacheStore < SassProcessor::CacheStore
|
29
|
+
def initialize(*args)
|
30
|
+
Deprecation.new.warn "SassCacheStore is deprecated please use SassProcessor::CacheStore instead"
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
29
34
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sprockets/autoload'
|
2
|
+
require 'sprockets/digest_utils'
|
2
3
|
|
3
4
|
module Sprockets
|
4
5
|
# Public: Sass CSS minifier.
|
@@ -34,26 +35,17 @@ module Sprockets
|
|
34
35
|
attr_reader :cache_key
|
35
36
|
|
36
37
|
def initialize(options = {})
|
37
|
-
@options =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
@options = {
|
39
|
+
syntax: :scss,
|
40
|
+
cache: false,
|
41
|
+
read_cache: false,
|
42
|
+
style: :compressed
|
43
|
+
}.merge(options).freeze
|
44
|
+
@cache_key = "#{self.class.name}:#{Autoload::Sass::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
|
44
45
|
end
|
45
46
|
|
46
47
|
def call(input)
|
47
|
-
|
48
|
-
input[:cache].fetch(@cache_key + [data]) do
|
49
|
-
options = {
|
50
|
-
syntax: :scss,
|
51
|
-
cache: false,
|
52
|
-
read_cache: false,
|
53
|
-
style: :compressed
|
54
|
-
}.merge(@options)
|
55
|
-
Autoload::Sass::Engine.new(data, options).render
|
56
|
-
end
|
48
|
+
Autoload::Sass::Engine.new(input[:data], @options).render
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|
@@ -39,17 +39,12 @@ module Sprockets
|
|
39
39
|
# Public: Initialize template with custom options.
|
40
40
|
#
|
41
41
|
# options - Hash
|
42
|
-
#
|
43
|
-
#
|
42
|
+
# cache_version - String custom cache version. Used to force a cache
|
43
|
+
# change after code changes are made to Sass Functions.
|
44
44
|
#
|
45
45
|
def initialize(options = {}, &block)
|
46
46
|
@cache_version = options[:cache_version]
|
47
|
-
@cache_key =
|
48
|
-
self.class.name,
|
49
|
-
VERSION,
|
50
|
-
Autoload::Sass::VERSION,
|
51
|
-
@cache_version
|
52
|
-
].freeze
|
47
|
+
@cache_key = "#{self.class.name}:#{VERSION}:#{Autoload::Sass::VERSION}:#{@cache_version}".freeze
|
53
48
|
|
54
49
|
@functions = Module.new do
|
55
50
|
include Functions
|
@@ -64,7 +59,7 @@ module Sprockets
|
|
64
59
|
options = {
|
65
60
|
filename: input[:filename],
|
66
61
|
syntax: self.class.syntax,
|
67
|
-
cache_store:
|
62
|
+
cache_store: build_cache_store(input, @cache_version),
|
68
63
|
load_paths: input[:environment].paths,
|
69
64
|
sprockets: {
|
70
65
|
context: context,
|
@@ -89,6 +84,18 @@ module Sprockets
|
|
89
84
|
context.metadata.merge(data: css, sass_dependencies: sass_dependencies)
|
90
85
|
end
|
91
86
|
|
87
|
+
# Public: Build the cache store to be used by the Sass engine.
|
88
|
+
#
|
89
|
+
# input - the input hash.
|
90
|
+
# version - the cache version.
|
91
|
+
#
|
92
|
+
# Override this method if you need to use a different cache than the
|
93
|
+
# Sprockets cache.
|
94
|
+
def build_cache_store(input, version)
|
95
|
+
CacheStore.new(input[:cache], version)
|
96
|
+
end
|
97
|
+
private :build_cache_store
|
98
|
+
|
92
99
|
# Public: Functions injected into Sass context during Sprockets evaluation.
|
93
100
|
#
|
94
101
|
# This module may be extended to add global functionality to all Sprockets
|
@@ -2,6 +2,18 @@ require 'sprockets/sass_processor'
|
|
2
2
|
|
3
3
|
module Sprockets
|
4
4
|
# Deprecated
|
5
|
-
SassTemplate
|
6
|
-
|
5
|
+
class SassTemplate < SassProcessor
|
6
|
+
def self.call(*args)
|
7
|
+
Deprecation.new.warn "SassTemplate is deprecated please use SassProcessor instead"
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Deprecated
|
13
|
+
class ScssTemplate < ScssProcessor
|
14
|
+
def self.call(*args)
|
15
|
+
Deprecation.new.warn "ScssTemplate is deprecated please use ScssProcessor instead"
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
7
19
|
end
|
data/lib/sprockets/server.rb
CHANGED
@@ -23,7 +23,7 @@ module Sprockets
|
|
23
23
|
start_time = Time.now.to_f
|
24
24
|
time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i }
|
25
25
|
|
26
|
-
if env['REQUEST_METHOD']
|
26
|
+
if !['GET', 'HEAD'].include?(env['REQUEST_METHOD'])
|
27
27
|
return method_not_allowed_response
|
28
28
|
end
|
29
29
|
|
@@ -39,7 +39,7 @@ module Sprockets
|
|
39
39
|
|
40
40
|
# URLs containing a `".."` are rejected for security reasons.
|
41
41
|
if forbidden_request?(path)
|
42
|
-
return forbidden_response
|
42
|
+
return forbidden_response(env)
|
43
43
|
end
|
44
44
|
|
45
45
|
# Look up the asset.
|
@@ -86,10 +86,10 @@ module Sprockets
|
|
86
86
|
not_modified_response(env, if_none_match)
|
87
87
|
when :not_found
|
88
88
|
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
|
89
|
-
not_found_response
|
89
|
+
not_found_response(env)
|
90
90
|
when :precondition_failed
|
91
91
|
logger.info "#{msg} 412 Precondition Failed (#{time_elapsed.call}ms)"
|
92
|
-
precondition_failed_response
|
92
|
+
precondition_failed_response(env)
|
93
93
|
end
|
94
94
|
rescue Exception => e
|
95
95
|
logger.error "Error compiling asset #{path}:"
|
@@ -115,12 +115,20 @@ module Sprockets
|
|
115
115
|
#
|
116
116
|
# http://example.org/assets/../../../etc/passwd
|
117
117
|
#
|
118
|
-
path.include?("..") || absolute_path?(path)
|
118
|
+
path.include?("..") || absolute_path?(path) || path.include?("://")
|
119
|
+
end
|
120
|
+
|
121
|
+
def head_request?(env)
|
122
|
+
env['REQUEST_METHOD'] == 'HEAD'
|
119
123
|
end
|
120
124
|
|
121
125
|
# Returns a 200 OK response tuple
|
122
126
|
def ok_response(asset, env)
|
123
|
-
|
127
|
+
if head_request?(env)
|
128
|
+
[ 200, headers(env, asset, 0), [] ]
|
129
|
+
else
|
130
|
+
[ 200, headers(env, asset, asset.length), asset ]
|
131
|
+
end
|
124
132
|
end
|
125
133
|
|
126
134
|
# Returns a 304 Not Modified response tuple
|
@@ -129,21 +137,33 @@ module Sprockets
|
|
129
137
|
end
|
130
138
|
|
131
139
|
# Returns a 403 Forbidden response tuple
|
132
|
-
def forbidden_response
|
133
|
-
|
140
|
+
def forbidden_response(env)
|
141
|
+
if head_request?(env)
|
142
|
+
[ 403, { "Content-Type" => "text/plain", "Content-Length" => "0" }, [] ]
|
143
|
+
else
|
144
|
+
[ 403, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Forbidden" ] ]
|
145
|
+
end
|
134
146
|
end
|
135
147
|
|
136
148
|
# Returns a 404 Not Found response tuple
|
137
|
-
def not_found_response
|
138
|
-
|
149
|
+
def not_found_response(env)
|
150
|
+
if head_request?(env)
|
151
|
+
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "0", "X-Cascade" => "pass" }, [] ]
|
152
|
+
else
|
153
|
+
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "9", "X-Cascade" => "pass" }, [ "Not found" ] ]
|
154
|
+
end
|
139
155
|
end
|
140
156
|
|
141
157
|
def method_not_allowed_response
|
142
158
|
[ 405, { "Content-Type" => "text/plain", "Content-Length" => "18" }, [ "Method Not Allowed" ] ]
|
143
159
|
end
|
144
160
|
|
145
|
-
def precondition_failed_response
|
146
|
-
|
161
|
+
def precondition_failed_response(env)
|
162
|
+
if head_request?(env)
|
163
|
+
[ 412, { "Content-Type" => "text/plain", "Content-Length" => "0", "X-Cascade" => "pass" }, [] ]
|
164
|
+
else
|
165
|
+
[ 412, { "Content-Type" => "text/plain", "Content-Length" => "19", "X-Cascade" => "pass" }, [ "Precondition Failed" ] ]
|
166
|
+
end
|
147
167
|
end
|
148
168
|
|
149
169
|
# Returns a JavaScript response that re-throws a Ruby exception
|
@@ -231,11 +251,11 @@ module Sprockets
|
|
231
251
|
# If the request url contains a fingerprint, set a long
|
232
252
|
# expires on the response
|
233
253
|
if path_fingerprint(env["PATH_INFO"])
|
234
|
-
headers["Cache-Control"]
|
254
|
+
headers["Cache-Control"] += ", max-age=31536000"
|
235
255
|
|
236
256
|
# Otherwise set `must-revalidate` since the asset could be modified.
|
237
257
|
else
|
238
|
-
headers["Cache-Control"]
|
258
|
+
headers["Cache-Control"] += ", must-revalidate"
|
239
259
|
headers["Vary"] = "Accept-Encoding"
|
240
260
|
end
|
241
261
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sprockets/autoload'
|
2
|
+
require 'sprockets/digest_utils'
|
2
3
|
|
3
4
|
module Sprockets
|
4
5
|
# Public: Uglifier/Uglify compressor.
|
@@ -40,24 +41,16 @@ module Sprockets
|
|
40
41
|
options[:copyright] ||= false
|
41
42
|
else
|
42
43
|
# Uglifier >= 2.x
|
43
|
-
options[:
|
44
|
+
options[:comments] ||= :none
|
44
45
|
end
|
45
46
|
|
46
|
-
@
|
47
|
-
|
48
|
-
@cache_key = [
|
49
|
-
self.class.name,
|
50
|
-
Autoload::Uglifier::VERSION,
|
51
|
-
VERSION,
|
52
|
-
options
|
53
|
-
].freeze
|
47
|
+
@options = options
|
48
|
+
@cache_key = "#{self.class.name}:#{Autoload::Uglifier::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
|
54
49
|
end
|
55
50
|
|
56
51
|
def call(input)
|
57
|
-
|
58
|
-
input[:
|
59
|
-
@uglifier.compile(data)
|
60
|
-
end
|
52
|
+
@uglifier ||= Autoload::Uglifier.new(@options)
|
53
|
+
@uglifier.compile(input[:data])
|
61
54
|
end
|
62
55
|
end
|
63
56
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'sprockets/uri_utils'
|
2
|
+
require 'sprockets/uri_tar'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
# Internal: Used to parse and store the URI to an unloaded asset
|
6
|
+
# Generates keys used to store and retrieve items from cache
|
7
|
+
class UnloadedAsset
|
8
|
+
|
9
|
+
# Internal: Initialize object for generating cache keys
|
10
|
+
#
|
11
|
+
# uri - A String containing complete URI to a file including scheme
|
12
|
+
# and full path such as
|
13
|
+
# "file:///Path/app/assets/js/app.js?type=application/javascript"
|
14
|
+
# env - The current "environment" that assets are being loaded into.
|
15
|
+
# We need it so we know where the +root+ (directory where sprockets
|
16
|
+
# is being invoked). We also need for the `file_digest` method,
|
17
|
+
# since, for some strange reason, memoization is provided by
|
18
|
+
# overriding methods such as `stat` in the `PathUtils` module.
|
19
|
+
#
|
20
|
+
# Returns UnloadedAsset.
|
21
|
+
def initialize(uri, env)
|
22
|
+
@uri = uri.to_s
|
23
|
+
@env = env
|
24
|
+
@compressed_path = URITar.new(uri, env).compressed_path
|
25
|
+
@params = nil # lazy loaded
|
26
|
+
@filename = nil # lazy loaded
|
27
|
+
end
|
28
|
+
attr_reader :compressed_path, :uri
|
29
|
+
|
30
|
+
# Internal: Full file path without schema
|
31
|
+
#
|
32
|
+
# This returns a string containing the full path to the asset without the schema.
|
33
|
+
# Information is loaded lazilly since we want `UnloadedAsset.new(dep, self).relative_path`
|
34
|
+
# to be fast. Calling this method the first time allocates an array and a hash.
|
35
|
+
#
|
36
|
+
# Example
|
37
|
+
#
|
38
|
+
# If the URI is `file:///Full/path/app/assets/javascripts/application.js"` then the
|
39
|
+
# filename would be `"/Full/path/app/assets/javascripts/application.js"`
|
40
|
+
#
|
41
|
+
# Returns a String.
|
42
|
+
def filename
|
43
|
+
unless @filename
|
44
|
+
load_file_params
|
45
|
+
end
|
46
|
+
@filename
|
47
|
+
end
|
48
|
+
|
49
|
+
# Internal: Hash of param values
|
50
|
+
#
|
51
|
+
# This information is generated and used internally by sprockets.
|
52
|
+
# Known keys include `:type` which store the asset's mime-type, `:id` which is a fully resolved
|
53
|
+
# digest for the asset (includes dependency digest as opposed to a digest of only file contents)
|
54
|
+
# and `:pipeline`. Hash may be empty.
|
55
|
+
#
|
56
|
+
# Example
|
57
|
+
#
|
58
|
+
# If the URI is `file:///Full/path/app/assets/javascripts/application.js"type=application/javascript`
|
59
|
+
# Then the params would be `{type: "application/javascript"}`
|
60
|
+
#
|
61
|
+
# Returns a Hash.
|
62
|
+
def params
|
63
|
+
unless @params
|
64
|
+
load_file_params
|
65
|
+
end
|
66
|
+
@params
|
67
|
+
end
|
68
|
+
|
69
|
+
# Internal: Key of asset
|
70
|
+
#
|
71
|
+
# Used to retrieve an asset from the cache based on "compressed" path to asset.
|
72
|
+
# A "compressed" path can either be relative to the root of the project or an
|
73
|
+
# absolute path.
|
74
|
+
#
|
75
|
+
# Returns a String.
|
76
|
+
def asset_key
|
77
|
+
"asset-uri:#{compressed_path}"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Public: Dependency History key
|
81
|
+
#
|
82
|
+
# Used to retrieve an array of "histories" each of which contain a set of stored dependencies
|
83
|
+
# for a given asset path and filename digest.
|
84
|
+
#
|
85
|
+
# A dependency can refer to either an asset i.e. index.js
|
86
|
+
# may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
|
87
|
+
# compilation, such as the VERSION of sprockets (i.e. the environment) and what "processors"
|
88
|
+
# are used.
|
89
|
+
#
|
90
|
+
# For example a history array with one Set of dependencies may look like:
|
91
|
+
#
|
92
|
+
# [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css",
|
93
|
+
# "file-digest:///Full/path/app/assets/stylesheets/application.css",
|
94
|
+
# "processors:type=text/css&file_type=text/css&pipeline=self",
|
95
|
+
# "file-digest:///Full/path/app/assets/stylesheets"]]
|
96
|
+
#
|
97
|
+
# This method of asset lookup is used to ensure that none of the dependencies have been modified
|
98
|
+
# since last lookup. If one of them has, the key will be different and a new entry must be stored.
|
99
|
+
#
|
100
|
+
# URI depndencies are later converted to "compressed" paths
|
101
|
+
#
|
102
|
+
# Returns a String.
|
103
|
+
def dependency_history_key
|
104
|
+
"asset-uri-cache-dependencies:#{compressed_path}:#{ @env.file_digest(filename) }"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Internal: Digest key
|
108
|
+
#
|
109
|
+
# Used to retrieve a string containing the "compressed" path to an asset based on
|
110
|
+
# a digest. The digest is generated from dependencies stored via information stored in
|
111
|
+
# the `dependency_history_key` after each of the "dependencies" is "resolved" for example
|
112
|
+
# "environment-version" may be resolved to "environment-1.0-3.2.0" for version "3.2.0" of sprockets
|
113
|
+
#
|
114
|
+
# Returns a String.
|
115
|
+
def digest_key(digest)
|
116
|
+
"asset-uri-digest:#{compressed_path}:#{digest}"
|
117
|
+
end
|
118
|
+
|
119
|
+
# Internal: File digest key
|
120
|
+
#
|
121
|
+
# The digest for a given file won't change if the path and the stat time hasn't changed
|
122
|
+
# We can save time by not re-computing this information and storing it in the cache
|
123
|
+
#
|
124
|
+
# Returns a String.
|
125
|
+
def file_digest_key(stat)
|
126
|
+
"file_digest:#{compressed_path}:#{stat}"
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
# Internal: Parses uri into filename and params hash
|
131
|
+
#
|
132
|
+
# Returns Array with filename and params hash
|
133
|
+
def load_file_params
|
134
|
+
@filename, @params = URIUtils.parse_asset_uri(uri)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'sprockets/path_utils'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# Internal: used to "expand" and "compress" values for storage
|
5
|
+
class URITar
|
6
|
+
attr_reader :scheme, :root, :path
|
7
|
+
|
8
|
+
# Internal: Initialize object for compression or expansion
|
9
|
+
#
|
10
|
+
# uri - A String containing URI that may or may not contain the scheme
|
11
|
+
# env - The current "environment" that assets are being loaded into.
|
12
|
+
def initialize(uri, env)
|
13
|
+
@root = env.root
|
14
|
+
@env = env
|
15
|
+
uri = uri.to_s
|
16
|
+
if uri.include?("://".freeze)
|
17
|
+
@scheme, _, @path = uri.partition("://".freeze)
|
18
|
+
@scheme << "://".freeze
|
19
|
+
else
|
20
|
+
@scheme = "".freeze
|
21
|
+
@path = uri
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Internal: Converts full uri to a "compressed" uri
|
26
|
+
#
|
27
|
+
# If a uri is inside of an environment's root it will
|
28
|
+
# be shortened to be a relative path.
|
29
|
+
#
|
30
|
+
# If a uri is outside of the environment's root the original
|
31
|
+
# uri will be returned.
|
32
|
+
#
|
33
|
+
# Returns String
|
34
|
+
def compress
|
35
|
+
scheme + compressed_path
|
36
|
+
end
|
37
|
+
|
38
|
+
# Internal: Tells us if we are using an absolute path
|
39
|
+
#
|
40
|
+
# Nix* systems start with a `/` like /Users/schneems.
|
41
|
+
# Windows systems start with a drive letter than colon and slash
|
42
|
+
# like C:/Schneems.
|
43
|
+
def absolute_path?
|
44
|
+
PathUtils.absolute_path?(path)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: Convert a "compressed" uri to an absolute path
|
48
|
+
#
|
49
|
+
# If a uri is inside of the environment's root it will not
|
50
|
+
# start with a slash for example:
|
51
|
+
#
|
52
|
+
# file://this/is/a/relative/path
|
53
|
+
#
|
54
|
+
# If a uri is outside the root, it will start with a slash:
|
55
|
+
#
|
56
|
+
# file:///This/is/an/absolute/path
|
57
|
+
#
|
58
|
+
# Returns String
|
59
|
+
def expand
|
60
|
+
if absolute_path?
|
61
|
+
# Stored path was absolute, don't add root
|
62
|
+
scheme + path
|
63
|
+
else
|
64
|
+
if scheme.empty?
|
65
|
+
File.join(root, path)
|
66
|
+
else
|
67
|
+
# We always want to return an absolute uri,
|
68
|
+
# make sure the path starts with a slash.
|
69
|
+
scheme + File.join("/".freeze, root, path)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Internal: Returns "compressed" path
|
75
|
+
#
|
76
|
+
# If the input uri is relative to the environment root
|
77
|
+
# it will return a path relative to the environment root.
|
78
|
+
# Otherwise an absolute path will be returned.
|
79
|
+
#
|
80
|
+
# Only path information is returned, and not scheme.
|
81
|
+
#
|
82
|
+
# Returns String
|
83
|
+
def compressed_path
|
84
|
+
# windows
|
85
|
+
if !@root.start_with?("/".freeze) && path.start_with?("/".freeze)
|
86
|
+
consistent_root = "/".freeze + @root
|
87
|
+
else
|
88
|
+
consistent_root = @root
|
89
|
+
end
|
90
|
+
|
91
|
+
if compressed_path = PathUtils.split_subpath(consistent_root, path)
|
92
|
+
compressed_path
|
93
|
+
else
|
94
|
+
path
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|