sprockets 2.6.0 → 4.2.2
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 +7 -0
- data/CHANGELOG.md +118 -0
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +541 -289
- data/bin/sprockets +20 -7
- data/lib/rake/sprocketstask.rb +34 -17
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +158 -210
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +8 -0
- data/lib/sprockets/autoload/coffee_script.rb +8 -0
- data/lib/sprockets/autoload/eco.rb +8 -0
- data/lib/sprockets/autoload/ejs.rb +8 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +8 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +8 -0
- data/lib/sprockets/autoload/yui.rb +8 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/autoload.rb +16 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +89 -378
- data/lib/sprockets/bower.rb +61 -0
- data/lib/sprockets/bundle.rb +105 -0
- data/lib/sprockets/cache/file_store.rb +190 -14
- data/lib/sprockets/cache/memory_store.rb +84 -0
- data/lib/sprockets/cache/null_store.rb +54 -0
- data/lib/sprockets/cache.rb +271 -0
- data/lib/sprockets/cached_environment.rb +64 -0
- data/lib/sprockets/closure_compressor.rb +48 -0
- data/lib/sprockets/coffee_script_processor.rb +39 -0
- data/lib/sprockets/compressing.rb +134 -0
- data/lib/sprockets/configuration.rb +79 -0
- data/lib/sprockets/context.rb +166 -150
- data/lib/sprockets/dependencies.rb +74 -0
- data/lib/sprockets/digest_utils.rb +197 -0
- data/lib/sprockets/directive_processor.rb +241 -215
- data/lib/sprockets/eco_processor.rb +33 -0
- data/lib/sprockets/ejs_processor.rb +32 -0
- data/lib/sprockets/encoding_utils.rb +261 -0
- data/lib/sprockets/environment.rb +23 -64
- data/lib/sprockets/erb_processor.rb +43 -0
- data/lib/sprockets/errors.rb +5 -13
- data/lib/sprockets/exporters/base.rb +71 -0
- data/lib/sprockets/exporters/file_exporter.rb +24 -0
- data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
- data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
- data/lib/sprockets/exporting.rb +73 -0
- data/lib/sprockets/file_reader.rb +16 -0
- data/lib/sprockets/http_utils.rb +135 -0
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +36 -19
- data/lib/sprockets/loader.rb +347 -0
- data/lib/sprockets/manifest.rb +228 -112
- data/lib/sprockets/manifest_utils.rb +48 -0
- data/lib/sprockets/mime.rb +78 -31
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +77 -0
- data/lib/sprockets/path_digest_utils.rb +48 -0
- data/lib/sprockets/path_utils.rb +367 -0
- data/lib/sprockets/paths.rb +43 -19
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +146 -164
- data/lib/sprockets/processor_utils.rb +170 -0
- data/lib/sprockets/resolve.rb +295 -0
- data/lib/sprockets/sass_cache_store.rb +20 -15
- data/lib/sprockets/sass_compressor.rb +55 -10
- data/lib/sprockets/sass_functions.rb +3 -70
- data/lib/sprockets/sass_importer.rb +3 -29
- data/lib/sprockets/sass_processor.rb +313 -0
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +159 -91
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +173 -0
- data/lib/sprockets/uglifier_compressor.rb +66 -0
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +194 -0
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +193 -52
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +56 -0
- data/lib/sprockets.rb +217 -75
- metadata +272 -117
- data/lib/sprockets/asset_attributes.rb +0 -131
- data/lib/sprockets/bundled_asset.rb +0 -80
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- data/lib/sprockets/eco_template.rb +0 -38
- data/lib/sprockets/ejs_template.rb +0 -37
- data/lib/sprockets/engines.rb +0 -74
- data/lib/sprockets/index.rb +0 -99
- data/lib/sprockets/processed_asset.rb +0 -152
- data/lib/sprockets/processor.rb +0 -32
- data/lib/sprockets/safety_colons.rb +0 -28
- data/lib/sprockets/sass_template.rb +0 -60
- data/lib/sprockets/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -58
data/lib/sprockets/server.rb
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'set'
|
|
1
3
|
require 'time'
|
|
2
|
-
require '
|
|
4
|
+
require 'rack'
|
|
3
5
|
|
|
4
6
|
module Sprockets
|
|
5
7
|
# `Server` is a concern mixed into `Environment` and
|
|
6
|
-
# `
|
|
8
|
+
# `CachedEnvironment` that provides a Rack compatible `call`
|
|
7
9
|
# interface and url generation helpers.
|
|
8
10
|
module Server
|
|
11
|
+
# Supported HTTP request methods.
|
|
12
|
+
ALLOWED_REQUEST_METHODS = ['GET', 'HEAD'].to_set.freeze
|
|
13
|
+
|
|
14
|
+
# :stopdoc:
|
|
15
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
|
|
16
|
+
X_CASCADE = "X-Cascade"
|
|
17
|
+
VARY = "Vary"
|
|
18
|
+
else
|
|
19
|
+
X_CASCADE = "x-cascade"
|
|
20
|
+
VARY = "vary"
|
|
21
|
+
end
|
|
22
|
+
# :startdoc:
|
|
23
|
+
|
|
9
24
|
# `call` implements the Rack 1.x specification which accepts an
|
|
10
25
|
# `env` Hash and returns a three item tuple with the status code,
|
|
11
26
|
# headers, and body.
|
|
@@ -23,19 +38,18 @@ module Sprockets
|
|
|
23
38
|
start_time = Time.now.to_f
|
|
24
39
|
time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i }
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
unless ALLOWED_REQUEST_METHODS.include? env['REQUEST_METHOD']
|
|
42
|
+
return method_not_allowed_response
|
|
43
|
+
end
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
env['rack.session.options'] ||= {}
|
|
30
|
-
env['rack.session.options'][:defer] = true
|
|
31
|
-
env['rack.session.options'][:skip] = true
|
|
45
|
+
msg = "Served asset #{env['PATH_INFO']} -"
|
|
32
46
|
|
|
33
47
|
# Extract the path from everything after the leading slash
|
|
34
|
-
|
|
48
|
+
full_path = Rack::Utils.unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
|
|
49
|
+
path = full_path
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return forbidden_response
|
|
51
|
+
unless path.valid_encoding?
|
|
52
|
+
return bad_request_response(env)
|
|
39
53
|
end
|
|
40
54
|
|
|
41
55
|
# Strip fingerprint
|
|
@@ -43,39 +57,69 @@ module Sprockets
|
|
|
43
57
|
path = path.sub("-#{fingerprint}", '')
|
|
44
58
|
end
|
|
45
59
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if asset.nil?
|
|
51
|
-
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
|
|
60
|
+
# URLs containing a `".."` are rejected for security reasons.
|
|
61
|
+
if forbidden_request?(path)
|
|
62
|
+
return forbidden_response(env)
|
|
63
|
+
end
|
|
52
64
|
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
if fingerprint
|
|
66
|
+
if_match = fingerprint
|
|
67
|
+
elsif env['HTTP_IF_MATCH']
|
|
68
|
+
if_match = env['HTTP_IF_MATCH'][/"(\w+)"$/, 1]
|
|
69
|
+
end
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
if env['HTTP_IF_NONE_MATCH']
|
|
72
|
+
if_none_match = env['HTTP_IF_NONE_MATCH'][/"(\w+)"$/, 1]
|
|
73
|
+
end
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
# Look up the asset.
|
|
76
|
+
asset = find_asset(path)
|
|
77
|
+
|
|
78
|
+
# Fallback to looking up the asset with the full path.
|
|
79
|
+
# This will make assets that are hashed with webpack or
|
|
80
|
+
# other js bundlers work consistently between production
|
|
81
|
+
# and development pipelines.
|
|
82
|
+
if asset.nil? && (asset = find_asset(full_path))
|
|
83
|
+
if_match = asset.etag if fingerprint
|
|
84
|
+
fingerprint = asset.etag
|
|
85
|
+
end
|
|
62
86
|
|
|
87
|
+
if asset.nil?
|
|
88
|
+
status = :not_found
|
|
89
|
+
elsif fingerprint && asset.etag != fingerprint
|
|
90
|
+
status = :not_found
|
|
91
|
+
elsif if_match && asset.etag != if_match
|
|
92
|
+
status = :precondition_failed
|
|
93
|
+
elsif if_none_match && asset.etag == if_none_match
|
|
94
|
+
status = :not_modified
|
|
63
95
|
else
|
|
64
|
-
|
|
96
|
+
status = :ok
|
|
97
|
+
end
|
|
65
98
|
|
|
66
|
-
|
|
99
|
+
case status
|
|
100
|
+
when :ok
|
|
101
|
+
logger.info "#{msg} 200 OK (#{time_elapsed.call}ms)"
|
|
67
102
|
ok_response(asset, env)
|
|
103
|
+
when :not_modified
|
|
104
|
+
logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)"
|
|
105
|
+
not_modified_response(env, if_none_match)
|
|
106
|
+
when :not_found
|
|
107
|
+
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
|
|
108
|
+
not_found_response(env)
|
|
109
|
+
when :precondition_failed
|
|
110
|
+
logger.info "#{msg} 412 Precondition Failed (#{time_elapsed.call}ms)"
|
|
111
|
+
precondition_failed_response(env)
|
|
68
112
|
end
|
|
69
113
|
rescue Exception => e
|
|
70
114
|
logger.error "Error compiling asset #{path}:"
|
|
71
115
|
logger.error "#{e.class.name}: #{e.message}"
|
|
72
116
|
|
|
73
|
-
case
|
|
74
|
-
when "
|
|
117
|
+
case File.extname(path)
|
|
118
|
+
when ".js"
|
|
75
119
|
# Re-throw JavaScript asset exceptions to the browser
|
|
76
120
|
logger.info "#{msg} 500 Internal Server Error\n\n"
|
|
77
121
|
return javascript_exception_response(e)
|
|
78
|
-
when "
|
|
122
|
+
when ".css"
|
|
79
123
|
# Display CSS asset exceptions in the browser
|
|
80
124
|
logger.info "#{msg} 500 Internal Server Error\n\n"
|
|
81
125
|
return css_exception_response(e)
|
|
@@ -90,25 +134,72 @@ module Sprockets
|
|
|
90
134
|
#
|
|
91
135
|
# http://example.org/assets/../../../etc/passwd
|
|
92
136
|
#
|
|
93
|
-
path.include?("..")
|
|
137
|
+
path.include?("..") || absolute_path?(path) || path.include?("://")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def head_request?(env)
|
|
141
|
+
env['REQUEST_METHOD'] == 'HEAD'
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Returns a 200 OK response tuple
|
|
145
|
+
def ok_response(asset, env)
|
|
146
|
+
if head_request?(env)
|
|
147
|
+
[ 200, headers(env, asset, 0), [] ]
|
|
148
|
+
else
|
|
149
|
+
[ 200, headers(env, asset, asset.length), asset ]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Returns a 304 Not Modified response tuple
|
|
154
|
+
def not_modified_response(env, etag)
|
|
155
|
+
[ 304, cache_headers(env, etag), [] ]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Returns a 400 Forbidden response tuple
|
|
159
|
+
def bad_request_response(env)
|
|
160
|
+
if head_request?(env)
|
|
161
|
+
[ 400, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0" }, [] ]
|
|
162
|
+
else
|
|
163
|
+
[ 400, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "11" }, [ "Bad Request" ] ]
|
|
164
|
+
end
|
|
94
165
|
end
|
|
95
166
|
|
|
96
167
|
# Returns a 403 Forbidden response tuple
|
|
97
|
-
def forbidden_response
|
|
98
|
-
|
|
168
|
+
def forbidden_response(env)
|
|
169
|
+
if head_request?(env)
|
|
170
|
+
[ 403, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0" }, [] ]
|
|
171
|
+
else
|
|
172
|
+
[ 403, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "9" }, [ "Forbidden" ] ]
|
|
173
|
+
end
|
|
99
174
|
end
|
|
100
175
|
|
|
101
176
|
# Returns a 404 Not Found response tuple
|
|
102
|
-
def not_found_response
|
|
103
|
-
|
|
177
|
+
def not_found_response(env)
|
|
178
|
+
if head_request?(env)
|
|
179
|
+
[ 404, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0", X_CASCADE => "pass" }, [] ]
|
|
180
|
+
else
|
|
181
|
+
[ 404, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "9", X_CASCADE => "pass" }, [ "Not found" ] ]
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def method_not_allowed_response
|
|
186
|
+
[ 405, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "18" }, [ "Method Not Allowed" ] ]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def precondition_failed_response(env)
|
|
190
|
+
if head_request?(env)
|
|
191
|
+
[ 412, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "0", X_CASCADE => "pass" }, [] ]
|
|
192
|
+
else
|
|
193
|
+
[ 412, { Rack::CONTENT_TYPE => "text/plain", Rack::CONTENT_LENGTH => "19", X_CASCADE => "pass" }, [ "Precondition Failed" ] ]
|
|
194
|
+
end
|
|
104
195
|
end
|
|
105
196
|
|
|
106
197
|
# Returns a JavaScript response that re-throws a Ruby exception
|
|
107
198
|
# in the browser
|
|
108
199
|
def javascript_exception_response(exception)
|
|
109
|
-
err = "#{exception.class.name}: #{exception.message}"
|
|
200
|
+
err = "#{exception.class.name}: #{exception.message}\n (in #{exception.backtrace[0]})"
|
|
110
201
|
body = "throw Error(#{err.inspect})"
|
|
111
|
-
[ 200, {
|
|
202
|
+
[ 200, { Rack::CONTENT_TYPE => "application/javascript", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [ body ] ]
|
|
112
203
|
end
|
|
113
204
|
|
|
114
205
|
# Returns a CSS response that hides all elements on the page and
|
|
@@ -161,7 +252,7 @@ module Sprockets
|
|
|
161
252
|
}
|
|
162
253
|
CSS
|
|
163
254
|
|
|
164
|
-
[ 200, {
|
|
255
|
+
[ 200, { Rack::CONTENT_TYPE => "text/css; charset=utf-8", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [ body ] ]
|
|
165
256
|
end
|
|
166
257
|
|
|
167
258
|
# Escape special characters for use inside a CSS content("...") string
|
|
@@ -173,75 +264,52 @@ module Sprockets
|
|
|
173
264
|
gsub('/', '\\\\002f ')
|
|
174
265
|
end
|
|
175
266
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
env["HTTP_IF_NONE_MATCH"] == etag(asset)
|
|
179
|
-
end
|
|
267
|
+
def cache_headers(env, etag)
|
|
268
|
+
headers = {}
|
|
180
269
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
end
|
|
270
|
+
# Set caching headers
|
|
271
|
+
headers[Rack::CACHE_CONTROL] = +"public"
|
|
272
|
+
headers[Rack::ETAG] = %("#{etag}")
|
|
185
273
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
274
|
+
# If the request url contains a fingerprint, set a long
|
|
275
|
+
# expires on the response
|
|
276
|
+
if path_fingerprint(env["PATH_INFO"])
|
|
277
|
+
headers[Rack::CACHE_CONTROL] << ", max-age=31536000, immutable"
|
|
190
278
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
279
|
+
# Otherwise set `must-revalidate` since the asset could be modified.
|
|
280
|
+
else
|
|
281
|
+
headers[Rack::CACHE_CONTROL] << ", must-revalidate"
|
|
282
|
+
headers[VARY] = "Accept-Encoding"
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
headers
|
|
194
286
|
end
|
|
195
287
|
|
|
196
288
|
def headers(env, asset, length)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
# If the request url contains a fingerprint, set a long
|
|
208
|
-
# expires on the response
|
|
209
|
-
if path_fingerprint(env["PATH_INFO"])
|
|
210
|
-
headers["Cache-Control"] << ", max-age=31536000"
|
|
211
|
-
|
|
212
|
-
# Otherwise set `must-revalidate` since the asset could be modified.
|
|
213
|
-
else
|
|
214
|
-
headers["Cache-Control"] << ", must-revalidate"
|
|
289
|
+
headers = {}
|
|
290
|
+
|
|
291
|
+
# Set content length header
|
|
292
|
+
headers[Rack::CONTENT_LENGTH] = length.to_s
|
|
293
|
+
|
|
294
|
+
# Set content type header
|
|
295
|
+
if type = asset.content_type
|
|
296
|
+
# Set charset param for text/* mime types
|
|
297
|
+
if type.start_with?("text/") && asset.charset
|
|
298
|
+
type += "; charset=#{asset.charset}"
|
|
215
299
|
end
|
|
300
|
+
headers[Rack::CONTENT_TYPE] = type
|
|
216
301
|
end
|
|
302
|
+
|
|
303
|
+
headers.merge(cache_headers(env, asset.etag))
|
|
217
304
|
end
|
|
218
305
|
|
|
219
|
-
# Gets
|
|
306
|
+
# Gets ETag fingerprint.
|
|
220
307
|
#
|
|
221
308
|
# "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
|
|
222
309
|
# # => "0aa2105d29558f3eb790d411d7d8fb66"
|
|
223
310
|
#
|
|
224
311
|
def path_fingerprint(path)
|
|
225
|
-
path[/-([0-9a-
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# URI.unescape is deprecated on 1.9. We need to use URI::Parser
|
|
229
|
-
# if its available.
|
|
230
|
-
if defined? URI::DEFAULT_PARSER
|
|
231
|
-
def unescape(str)
|
|
232
|
-
str = URI::DEFAULT_PARSER.unescape(str)
|
|
233
|
-
str.force_encoding(Encoding.default_internal) if Encoding.default_internal
|
|
234
|
-
str
|
|
235
|
-
end
|
|
236
|
-
else
|
|
237
|
-
def unescape(str)
|
|
238
|
-
URI.unescape(str)
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
# Helper to quote the assets digest for use as an ETag.
|
|
243
|
-
def etag(asset)
|
|
244
|
-
%("#{asset.digest}")
|
|
312
|
+
path[/-([0-9a-zA-Z]{7,128})\.[^.]+\z/, 1]
|
|
245
313
|
end
|
|
246
314
|
end
|
|
247
315
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
module Sprockets
|
|
5
|
+
|
|
6
|
+
# The purpose of this class is to generate a source map file
|
|
7
|
+
# that can be read and understood by browsers.
|
|
8
|
+
#
|
|
9
|
+
# When a file is passed in it will have a `application/js-sourcemap+json`
|
|
10
|
+
# or `application/css-sourcemap+json` mime type. The filename will be
|
|
11
|
+
# match the original asset. The original asset is loaded. As it
|
|
12
|
+
# gets processed by Sprockets it will acquire all information
|
|
13
|
+
# needed to build a source map file in the `asset.to_hash[:metadata][:map]`
|
|
14
|
+
# key.
|
|
15
|
+
#
|
|
16
|
+
# The output is an asset with a properly formatted source map file:
|
|
17
|
+
#
|
|
18
|
+
# {
|
|
19
|
+
# "version": 3,
|
|
20
|
+
# "sources": ["foo.js"],
|
|
21
|
+
# "names": [ ],
|
|
22
|
+
# "mappings": "AAAA,GAAIA"
|
|
23
|
+
# }
|
|
24
|
+
#
|
|
25
|
+
class SourceMapProcessor
|
|
26
|
+
def self.call(input)
|
|
27
|
+
links = Set.new(input[:metadata][:links])
|
|
28
|
+
env = input[:environment]
|
|
29
|
+
|
|
30
|
+
uri, _ = env.resolve!(input[:filename], accept: self.original_content_type(input[:content_type]))
|
|
31
|
+
asset = env.load(uri)
|
|
32
|
+
map = asset.metadata[:map]
|
|
33
|
+
|
|
34
|
+
# TODO: Because of the default piplene hack we have to apply dependencies
|
|
35
|
+
# from compiled asset to the source map, otherwise the source map cache
|
|
36
|
+
# will never detect the changes from directives
|
|
37
|
+
dependencies = Set.new(input[:metadata][:dependencies])
|
|
38
|
+
dependencies.merge(asset.metadata[:dependencies])
|
|
39
|
+
|
|
40
|
+
map["file"] = PathUtils.split_subpath(input[:load_path], input[:filename])
|
|
41
|
+
sources = map["sections"] ? map["sections"].map { |s| s["map"]["sources"] }.flatten : map["sources"]
|
|
42
|
+
|
|
43
|
+
sources.each do |source|
|
|
44
|
+
source = PathUtils.join(File.dirname(map["file"]), source)
|
|
45
|
+
uri, _ = env.resolve!(source)
|
|
46
|
+
links << uri
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
json = JSON.generate(map)
|
|
50
|
+
|
|
51
|
+
{ data: json, links: links, dependencies: dependencies }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.original_content_type(source_map_content_type, error_when_not_found: true)
|
|
55
|
+
case source_map_content_type
|
|
56
|
+
when "application/js-sourcemap+json"
|
|
57
|
+
"application/javascript"
|
|
58
|
+
when "application/css-sourcemap+json"
|
|
59
|
+
"text/css"
|
|
60
|
+
else
|
|
61
|
+
fail(source_map_content_type) if error_when_not_found
|
|
62
|
+
source_map_content_type
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|