sprockets 3.7.3 → 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 +4 -4
- data/CHANGELOG.md +77 -259
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +527 -320
- data/bin/sprockets +11 -7
- data/lib/rake/sprocketstask.rb +9 -4
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +39 -27
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +1 -0
- data/lib/sprockets/autoload/coffee_script.rb +1 -0
- data/lib/sprockets/autoload/eco.rb +1 -0
- data/lib/sprockets/autoload/ejs.rb +1 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +1 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +1 -0
- data/lib/sprockets/autoload/yui.rb +1 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/autoload.rb +5 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +49 -12
- data/lib/sprockets/bower.rb +6 -3
- data/lib/sprockets/bundle.rb +41 -5
- data/lib/sprockets/cache/file_store.rb +25 -3
- data/lib/sprockets/cache/memory_store.rb +28 -10
- data/lib/sprockets/cache/null_store.rb +8 -0
- data/lib/sprockets/cache.rb +37 -2
- data/lib/sprockets/cached_environment.rb +15 -20
- data/lib/sprockets/closure_compressor.rb +1 -0
- data/lib/sprockets/coffee_script_processor.rb +19 -5
- data/lib/sprockets/compressing.rb +43 -3
- data/lib/sprockets/configuration.rb +5 -9
- data/lib/sprockets/context.rb +99 -25
- data/lib/sprockets/dependencies.rb +2 -1
- data/lib/sprockets/digest_utils.rb +35 -18
- data/lib/sprockets/directive_processor.rb +64 -38
- data/lib/sprockets/eco_processor.rb +2 -1
- data/lib/sprockets/ejs_processor.rb +2 -1
- data/lib/sprockets/encoding_utils.rb +2 -2
- data/lib/sprockets/environment.rb +9 -4
- data/lib/sprockets/erb_processor.rb +33 -32
- data/lib/sprockets/errors.rb +1 -0
- 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 +1 -0
- data/lib/sprockets/http_utils.rb +25 -7
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +11 -10
- data/lib/sprockets/loader.rb +91 -69
- data/lib/sprockets/manifest.rb +67 -64
- data/lib/sprockets/manifest_utils.rb +9 -6
- data/lib/sprockets/mime.rb +8 -62
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +3 -11
- data/lib/sprockets/path_digest_utils.rb +2 -1
- data/lib/sprockets/path_utils.rb +88 -8
- data/lib/sprockets/paths.rb +1 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +32 -62
- data/lib/sprockets/processor_utils.rb +28 -38
- data/lib/sprockets/resolve.rb +177 -93
- data/lib/sprockets/sass_cache_store.rb +2 -6
- data/lib/sprockets/sass_compressor.rb +13 -1
- data/lib/sprockets/sass_functions.rb +1 -0
- data/lib/sprockets/sass_importer.rb +1 -0
- data/lib/sprockets/sass_processor.rb +31 -10
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +63 -40
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +63 -35
- data/lib/sprockets/uglifier_compressor.rb +21 -11
- data/lib/sprockets/unloaded_asset.rb +13 -11
- data/lib/sprockets/uri_tar.rb +1 -0
- data/lib/sprockets/uri_utils.rb +19 -16
- data/lib/sprockets/utils/gzip.rb +46 -14
- data/lib/sprockets/utils.rb +64 -90
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +1 -0
- data/lib/sprockets.rb +102 -39
- metadata +148 -45
- data/lib/sprockets/coffee_script_template.rb +0 -17
- data/lib/sprockets/deprecation.rb +0 -90
- data/lib/sprockets/eco_template.rb +0 -17
- data/lib/sprockets/ejs_template.rb +0 -17
- data/lib/sprockets/engines.rb +0 -92
- data/lib/sprockets/erb_template.rb +0 -11
- data/lib/sprockets/legacy.rb +0 -330
- data/lib/sprockets/legacy_proc_processor.rb +0 -35
- data/lib/sprockets/legacy_tilt_processor.rb +0 -29
- data/lib/sprockets/sass_template.rb +0 -19
@@ -0,0 +1,483 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
require 'sprockets/path_utils'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
module SourceMapUtils
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Public: Transpose source maps into a standard format
|
10
|
+
#
|
11
|
+
# NOTE: Does not support index maps
|
12
|
+
#
|
13
|
+
# version => 3
|
14
|
+
# file => logical path
|
15
|
+
# sources => relative from filename
|
16
|
+
#
|
17
|
+
# Unnecessary attributes are removed
|
18
|
+
#
|
19
|
+
# Example
|
20
|
+
#
|
21
|
+
# map
|
22
|
+
# #=> {
|
23
|
+
# # "version" => 3,
|
24
|
+
# # "file" => "stdin",
|
25
|
+
# # "sourceRoot" => "",
|
26
|
+
# # "sourceContents" => "blah blah blah",
|
27
|
+
# # "sources" => [/root/logical/path.js],
|
28
|
+
# # "names" => [..],
|
29
|
+
# #}
|
30
|
+
# format_source_map(map, input)
|
31
|
+
# #=> {
|
32
|
+
# # "version" => 3,
|
33
|
+
# # "file" => "logical/path.js",
|
34
|
+
# # "sources" => ["path.js"],
|
35
|
+
# # "names" => [..],
|
36
|
+
# #}
|
37
|
+
def format_source_map(map, input)
|
38
|
+
filename = input[:filename]
|
39
|
+
load_path = input[:load_path]
|
40
|
+
load_paths = input[:environment].config[:paths]
|
41
|
+
mime_exts = input[:environment].config[:mime_exts]
|
42
|
+
pipeline_exts = input[:environment].config[:pipeline_exts]
|
43
|
+
file = PathUtils.split_subpath(load_path, filename)
|
44
|
+
{
|
45
|
+
"version" => 3,
|
46
|
+
"file" => file,
|
47
|
+
"mappings" => map["mappings"],
|
48
|
+
"sources" => map["sources"].map do |source|
|
49
|
+
source = URIUtils.split_file_uri(source)[2] if source.start_with? "file://"
|
50
|
+
source = PathUtils.join(File.dirname(filename), source) unless PathUtils.absolute_path?(source)
|
51
|
+
_, source = PathUtils.paths_split(load_paths, source)
|
52
|
+
source = PathUtils.relative_path_from(file, source)
|
53
|
+
PathUtils.set_pipeline(source, mime_exts, pipeline_exts, :source)
|
54
|
+
end,
|
55
|
+
"names" => map["names"]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: Concatenate two source maps.
|
60
|
+
#
|
61
|
+
# For an example, if two js scripts are concatenated, the individual source
|
62
|
+
# maps for those files can be concatenated to map back to the originals.
|
63
|
+
#
|
64
|
+
# Examples
|
65
|
+
#
|
66
|
+
# script3 = "#{script1}#{script2}"
|
67
|
+
# map3 = concat_source_maps(map1, map2)
|
68
|
+
#
|
69
|
+
# a - Source map hash
|
70
|
+
# b - Source map hash
|
71
|
+
#
|
72
|
+
# Returns a new source map hash.
|
73
|
+
def concat_source_maps(a, b)
|
74
|
+
return a || b unless a && b
|
75
|
+
a = make_index_map(a)
|
76
|
+
b = make_index_map(b)
|
77
|
+
|
78
|
+
offset = 0
|
79
|
+
if a["sections"].count != 0 && !a["sections"].last["map"]["mappings"].empty?
|
80
|
+
last_line_count = a["sections"].last["map"].delete("x_sprockets_linecount")
|
81
|
+
offset += last_line_count || 1
|
82
|
+
|
83
|
+
last_offset = a["sections"].last["offset"]["line"]
|
84
|
+
offset += last_offset
|
85
|
+
end
|
86
|
+
|
87
|
+
a["sections"] += b["sections"].map do |section|
|
88
|
+
{
|
89
|
+
"offset" => section["offset"].merge({ "line" => section["offset"]["line"] + offset }),
|
90
|
+
"map" => section["map"].merge({
|
91
|
+
"sources" => section["map"]["sources"].map do |source|
|
92
|
+
PathUtils.relative_path_from(a["file"], PathUtils.join(File.dirname(b["file"]), source))
|
93
|
+
end
|
94
|
+
})
|
95
|
+
}
|
96
|
+
end
|
97
|
+
a
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Converts source map to index map
|
101
|
+
#
|
102
|
+
# Example:
|
103
|
+
#
|
104
|
+
# map
|
105
|
+
# # => {
|
106
|
+
# "version" => 3,
|
107
|
+
# "file" => "..",
|
108
|
+
# "mappings" => "AAAA;AACA;..;AACA",
|
109
|
+
# "sources" => [..],
|
110
|
+
# "names" => [..]
|
111
|
+
# }
|
112
|
+
# make_index_map(map)
|
113
|
+
# # => {
|
114
|
+
# "version" => 3,
|
115
|
+
# "file" => "..",
|
116
|
+
# "sections" => [
|
117
|
+
# {
|
118
|
+
# "offset" => { "line" => 0, "column" => 0 },
|
119
|
+
# "map" => {
|
120
|
+
# "version" => 3,
|
121
|
+
# "file" => "..",
|
122
|
+
# "mappings" => "AAAA;AACA;..;AACA",
|
123
|
+
# "sources" => [..],
|
124
|
+
# "names" => [..]
|
125
|
+
# }
|
126
|
+
# }
|
127
|
+
# ]
|
128
|
+
# }
|
129
|
+
def make_index_map(map)
|
130
|
+
return map if map.key? "sections"
|
131
|
+
{
|
132
|
+
"version" => map["version"],
|
133
|
+
"file" => map["file"],
|
134
|
+
"sections" => [
|
135
|
+
{
|
136
|
+
"offset" => { "line" => 0, "column" => 0 },
|
137
|
+
"map" => map
|
138
|
+
}
|
139
|
+
]
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
# Public: Combine two separate source map transformations into a single
|
144
|
+
# mapping.
|
145
|
+
#
|
146
|
+
# Source transformations may happen in discrete steps producing separate
|
147
|
+
# source maps. These steps can be combined into a single mapping back to
|
148
|
+
# the source.
|
149
|
+
#
|
150
|
+
# For an example, CoffeeScript may transform a file producing a map. Then
|
151
|
+
# Uglifier processes the result and produces another map. The CoffeeScript
|
152
|
+
# map can be combined with the Uglifier map so the source lines of the
|
153
|
+
# minified output can be traced back to the original CoffeeScript file.
|
154
|
+
#
|
155
|
+
# Returns a source map hash.
|
156
|
+
def combine_source_maps(first, second)
|
157
|
+
return second unless first
|
158
|
+
|
159
|
+
_first = decode_source_map(first)
|
160
|
+
_second = decode_source_map(second)
|
161
|
+
|
162
|
+
new_mappings = []
|
163
|
+
|
164
|
+
_second[:mappings].each do |m|
|
165
|
+
first_line = bsearch_mappings(_first[:mappings], m[:original])
|
166
|
+
new_mappings << first_line.merge(generated: m[:generated]) if first_line
|
167
|
+
end
|
168
|
+
|
169
|
+
_first[:mappings] = new_mappings
|
170
|
+
|
171
|
+
encode_source_map(_first)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Public: Decompress source map
|
175
|
+
#
|
176
|
+
# Example:
|
177
|
+
#
|
178
|
+
# decode_source_map(map)
|
179
|
+
# # => {
|
180
|
+
# version: 3,
|
181
|
+
# file: "..",
|
182
|
+
# mappings: [
|
183
|
+
# { source: "..", generated: [0, 0], original: [0, 0], name: ".."}, ..
|
184
|
+
# ],
|
185
|
+
# sources: [..],
|
186
|
+
# names: [..]
|
187
|
+
# }
|
188
|
+
#
|
189
|
+
# map - Source map hash (v3 spec)
|
190
|
+
#
|
191
|
+
# Returns an uncompressed source map hash
|
192
|
+
def decode_source_map(map)
|
193
|
+
return nil unless map
|
194
|
+
|
195
|
+
mappings, sources, names = [], [], []
|
196
|
+
if map["sections"]
|
197
|
+
map["sections"].each do |s|
|
198
|
+
mappings += decode_source_map(s["map"])[:mappings].each do |m|
|
199
|
+
m[:generated][0] += s["offset"]["line"]
|
200
|
+
m[:generated][1] += s["offset"]["column"]
|
201
|
+
end
|
202
|
+
sources |= s["map"]["sources"]
|
203
|
+
names |= s["map"]["names"]
|
204
|
+
end
|
205
|
+
else
|
206
|
+
mappings = decode_vlq_mappings(map["mappings"], sources: map["sources"], names: map["names"])
|
207
|
+
sources = map["sources"]
|
208
|
+
names = map["names"]
|
209
|
+
end
|
210
|
+
{
|
211
|
+
version: 3,
|
212
|
+
file: map["file"],
|
213
|
+
mappings: mappings,
|
214
|
+
sources: sources,
|
215
|
+
names: names
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
# Public: Compress source map
|
220
|
+
#
|
221
|
+
# Example:
|
222
|
+
#
|
223
|
+
# encode_source_map(map)
|
224
|
+
# # => {
|
225
|
+
# "version" => 3,
|
226
|
+
# "file" => "..",
|
227
|
+
# "mappings" => "AAAA;AACA;..;AACA",
|
228
|
+
# "sources" => [..],
|
229
|
+
# "names" => [..]
|
230
|
+
# }
|
231
|
+
#
|
232
|
+
# map - Source map hash (uncompressed)
|
233
|
+
#
|
234
|
+
# Returns a compressed source map hash according to source map spec v3
|
235
|
+
def encode_source_map(map)
|
236
|
+
return nil unless map
|
237
|
+
{
|
238
|
+
"version" => map[:version],
|
239
|
+
"file" => map[:file],
|
240
|
+
"mappings" => encode_vlq_mappings(map[:mappings], sources: map[:sources], names: map[:names]),
|
241
|
+
"sources" => map[:sources],
|
242
|
+
"names" => map[:names]
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
# Public: Compare two source map offsets.
|
247
|
+
#
|
248
|
+
# Compatible with Array#sort.
|
249
|
+
#
|
250
|
+
# a - Array [line, column]
|
251
|
+
# b - Array [line, column]
|
252
|
+
#
|
253
|
+
# Returns -1 if a < b, 0 if a == b and 1 if a > b.
|
254
|
+
def compare_source_offsets(a, b)
|
255
|
+
diff = a[0] - b[0]
|
256
|
+
diff = a[1] - b[1] if diff == 0
|
257
|
+
|
258
|
+
if diff < 0
|
259
|
+
-1
|
260
|
+
elsif diff > 0
|
261
|
+
1
|
262
|
+
else
|
263
|
+
0
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Public: Search Array of mappings for closest offset.
|
268
|
+
#
|
269
|
+
# mappings - Array of mapping Hash objects
|
270
|
+
# offset - Array [line, column]
|
271
|
+
#
|
272
|
+
# Returns mapping Hash object.
|
273
|
+
def bsearch_mappings(mappings, offset, from = 0, to = mappings.size - 1)
|
274
|
+
mid = (from + to) / 2
|
275
|
+
|
276
|
+
if from > to
|
277
|
+
return from < 1 ? nil : mappings[from-1]
|
278
|
+
end
|
279
|
+
|
280
|
+
case compare_source_offsets(offset, mappings[mid][:generated])
|
281
|
+
when 0
|
282
|
+
mappings[mid]
|
283
|
+
when -1
|
284
|
+
bsearch_mappings(mappings, offset, from, mid - 1)
|
285
|
+
when 1
|
286
|
+
bsearch_mappings(mappings, offset, mid + 1, to)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Public: Decode VLQ mappings and match up sources and symbol names.
|
291
|
+
#
|
292
|
+
# str - VLQ string from 'mappings' attribute
|
293
|
+
# sources - Array of Strings from 'sources' attribute
|
294
|
+
# names - Array of Strings from 'names' attribute
|
295
|
+
#
|
296
|
+
# Returns an Array of Mappings.
|
297
|
+
def decode_vlq_mappings(str, sources: [], names: [])
|
298
|
+
mappings = []
|
299
|
+
|
300
|
+
source_id = 0
|
301
|
+
original_line = 1
|
302
|
+
original_column = 0
|
303
|
+
name_id = 0
|
304
|
+
|
305
|
+
vlq_decode_mappings(str).each_with_index do |group, index|
|
306
|
+
generated_column = 0
|
307
|
+
generated_line = index + 1
|
308
|
+
|
309
|
+
group.each do |segment|
|
310
|
+
generated_column += segment[0]
|
311
|
+
generated = [generated_line, generated_column]
|
312
|
+
|
313
|
+
if segment.size >= 4
|
314
|
+
source_id += segment[1]
|
315
|
+
original_line += segment[2]
|
316
|
+
original_column += segment[3]
|
317
|
+
|
318
|
+
source = sources[source_id]
|
319
|
+
original = [original_line, original_column]
|
320
|
+
else
|
321
|
+
# TODO: Research this case
|
322
|
+
next
|
323
|
+
end
|
324
|
+
|
325
|
+
if segment[4]
|
326
|
+
name_id += segment[4]
|
327
|
+
name = names[name_id]
|
328
|
+
end
|
329
|
+
|
330
|
+
mapping = {source: source, generated: generated, original: original}
|
331
|
+
mapping[:name] = name if name
|
332
|
+
mappings << mapping
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
mappings
|
337
|
+
end
|
338
|
+
|
339
|
+
# Public: Encode mappings Hash into a VLQ encoded String.
|
340
|
+
#
|
341
|
+
# mappings - Array of Hash mapping objects
|
342
|
+
# sources - Array of String sources (default: mappings source order)
|
343
|
+
# names - Array of String names (default: mappings name order)
|
344
|
+
#
|
345
|
+
# Returns a VLQ encoded String.
|
346
|
+
def encode_vlq_mappings(mappings, sources: nil, names: nil)
|
347
|
+
sources ||= mappings.map { |m| m[:source] }.uniq.compact
|
348
|
+
names ||= mappings.map { |m| m[:name] }.uniq.compact
|
349
|
+
|
350
|
+
sources_index = Hash[sources.each_with_index.to_a]
|
351
|
+
names_index = Hash[names.each_with_index.to_a]
|
352
|
+
|
353
|
+
source_id = 0
|
354
|
+
source_line = 1
|
355
|
+
source_column = 0
|
356
|
+
name_id = 0
|
357
|
+
|
358
|
+
by_lines = mappings.group_by { |m| m[:generated][0] }
|
359
|
+
|
360
|
+
ary = (1..(by_lines.keys.max || 1)).map do |line|
|
361
|
+
generated_column = 0
|
362
|
+
|
363
|
+
(by_lines[line] || []).map do |mapping|
|
364
|
+
group = []
|
365
|
+
group << mapping[:generated][1] - generated_column
|
366
|
+
group << sources_index[mapping[:source]] - source_id
|
367
|
+
group << mapping[:original][0] - source_line
|
368
|
+
group << mapping[:original][1] - source_column
|
369
|
+
group << names_index[mapping[:name]] - name_id if mapping[:name]
|
370
|
+
|
371
|
+
generated_column = mapping[:generated][1]
|
372
|
+
source_id = sources_index[mapping[:source]]
|
373
|
+
source_line = mapping[:original][0]
|
374
|
+
source_column = mapping[:original][1]
|
375
|
+
name_id = names_index[mapping[:name]] if mapping[:name]
|
376
|
+
|
377
|
+
group
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
vlq_encode_mappings(ary)
|
382
|
+
end
|
383
|
+
|
384
|
+
# Public: Base64 VLQ encoding
|
385
|
+
#
|
386
|
+
# Adopted from ConradIrwin/ruby-source_map
|
387
|
+
# https://github.com/ConradIrwin/ruby-source_map/blob/master/lib/source_map/vlq.rb
|
388
|
+
#
|
389
|
+
# Resources
|
390
|
+
#
|
391
|
+
# http://en.wikipedia.org/wiki/Variable-length_quantity
|
392
|
+
# https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
393
|
+
# https://github.com/mozilla/source-map/blob/master/lib/source-map/base64-vlq.js
|
394
|
+
#
|
395
|
+
VLQ_BASE_SHIFT = 5
|
396
|
+
VLQ_BASE = 1 << VLQ_BASE_SHIFT
|
397
|
+
VLQ_BASE_MASK = VLQ_BASE - 1
|
398
|
+
VLQ_CONTINUATION_BIT = VLQ_BASE
|
399
|
+
|
400
|
+
BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
|
401
|
+
BASE64_VALUES = (0...64).inject({}) { |h, i| h[BASE64_DIGITS[i]] = i; h }
|
402
|
+
|
403
|
+
# Public: Encode a list of numbers into a compact VLQ string.
|
404
|
+
#
|
405
|
+
# ary - An Array of Integers
|
406
|
+
#
|
407
|
+
# Returns a VLQ String.
|
408
|
+
def vlq_encode(ary)
|
409
|
+
result = []
|
410
|
+
ary.each do |n|
|
411
|
+
vlq = n < 0 ? ((-n) << 1) + 1 : n << 1
|
412
|
+
loop do
|
413
|
+
digit = vlq & VLQ_BASE_MASK
|
414
|
+
vlq >>= VLQ_BASE_SHIFT
|
415
|
+
digit |= VLQ_CONTINUATION_BIT if vlq > 0
|
416
|
+
result << BASE64_DIGITS[digit]
|
417
|
+
|
418
|
+
break unless vlq > 0
|
419
|
+
end
|
420
|
+
end
|
421
|
+
result.join
|
422
|
+
end
|
423
|
+
|
424
|
+
# Public: Decode a VLQ string.
|
425
|
+
#
|
426
|
+
# str - VLQ encoded String
|
427
|
+
#
|
428
|
+
# Returns an Array of Integers.
|
429
|
+
def vlq_decode(str)
|
430
|
+
result = []
|
431
|
+
shift = 0
|
432
|
+
value = 0
|
433
|
+
i = 0
|
434
|
+
|
435
|
+
while i < str.size do
|
436
|
+
digit = BASE64_VALUES[str[i]]
|
437
|
+
raise ArgumentError unless digit
|
438
|
+
continuation = (digit & VLQ_CONTINUATION_BIT) != 0
|
439
|
+
digit &= VLQ_BASE_MASK
|
440
|
+
value += digit << shift
|
441
|
+
if continuation
|
442
|
+
shift += VLQ_BASE_SHIFT
|
443
|
+
else
|
444
|
+
result << ((value & 1) == 1 ? -(value >> 1) : value >> 1)
|
445
|
+
value = shift = 0
|
446
|
+
end
|
447
|
+
i += 1
|
448
|
+
end
|
449
|
+
result
|
450
|
+
end
|
451
|
+
|
452
|
+
# Public: Encode a mapping array into a compact VLQ string.
|
453
|
+
#
|
454
|
+
# ary - Two dimensional Array of Integers.
|
455
|
+
#
|
456
|
+
# Returns a VLQ encoded String separated by , and ;.
|
457
|
+
def vlq_encode_mappings(ary)
|
458
|
+
ary.map { |group|
|
459
|
+
group.map { |segment|
|
460
|
+
vlq_encode(segment)
|
461
|
+
}.join(',')
|
462
|
+
}.join(';')
|
463
|
+
end
|
464
|
+
|
465
|
+
# Public: Decode a VLQ string into mapping numbers.
|
466
|
+
#
|
467
|
+
# str - VLQ encoded String
|
468
|
+
#
|
469
|
+
# Returns an two dimensional Array of Integers.
|
470
|
+
def vlq_decode_mappings(str)
|
471
|
+
mappings = []
|
472
|
+
|
473
|
+
str.split(';').each_with_index do |group, index|
|
474
|
+
mappings[index] = []
|
475
|
+
group.split(',').each do |segment|
|
476
|
+
mappings[index] << vlq_decode(segment)
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
mappings
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/http_utils'
|
2
3
|
require 'sprockets/processor_utils'
|
3
4
|
require 'sprockets/utils'
|
@@ -18,6 +19,8 @@ module Sprockets
|
|
18
19
|
config[:transformers]
|
19
20
|
end
|
20
21
|
|
22
|
+
Transformer = Struct.new :from, :to, :proc
|
23
|
+
|
21
24
|
# Public: Register a transformer from and to a mime type.
|
22
25
|
#
|
23
26
|
# from - String mime type
|
@@ -33,10 +36,30 @@ module Sprockets
|
|
33
36
|
#
|
34
37
|
# Returns nothing.
|
35
38
|
def register_transformer(from, to, proc)
|
36
|
-
self.config = hash_reassoc(config, :registered_transformers
|
37
|
-
transformers.
|
39
|
+
self.config = hash_reassoc(config, :registered_transformers) do |transformers|
|
40
|
+
transformers << Transformer.new(from, to, proc)
|
41
|
+
end
|
42
|
+
compute_transformers!(self.config[:registered_transformers])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Internal: Register transformer for existing type adding a suffix.
|
46
|
+
#
|
47
|
+
# types - Array of existing mime type Strings
|
48
|
+
# type_format - String suffix formatting string
|
49
|
+
# extname - String extension to append
|
50
|
+
# processor - Callable block that accepts an input Hash.
|
51
|
+
#
|
52
|
+
# Returns nothing.
|
53
|
+
def register_transformer_suffix(types, type_format, extname, processor)
|
54
|
+
Array(types).each do |type|
|
55
|
+
extensions, charset = mime_types[type].values_at(:extensions, :charset)
|
56
|
+
parts = type.split('/')
|
57
|
+
suffix_type = type_format.sub('\1', parts[0]).sub('\2', parts[1])
|
58
|
+
extensions = extensions.map { |ext| "#{ext}#{extname}" }
|
59
|
+
|
60
|
+
register_mime_type(suffix_type, extensions: extensions, charset: charset)
|
61
|
+
register_transformer(suffix_type, type, processor)
|
38
62
|
end
|
39
|
-
compute_transformers!
|
40
63
|
end
|
41
64
|
|
42
65
|
# Internal: Resolve target mime type that the source type should be
|
@@ -89,53 +112,58 @@ module Sprockets
|
|
89
112
|
# types - Array of mime type steps
|
90
113
|
#
|
91
114
|
# Returns Processor.
|
92
|
-
def compose_transformers(transformers, types)
|
115
|
+
def compose_transformers(transformers, types, preprocessors, postprocessors)
|
93
116
|
if types.length < 2
|
94
117
|
raise ArgumentError, "too few transform types: #{types.inspect}"
|
95
118
|
end
|
96
119
|
|
97
|
-
|
98
|
-
processors = []
|
99
|
-
|
100
|
-
loop do
|
101
|
-
src = types[i]
|
102
|
-
dst = types[i+1]
|
103
|
-
break unless src && dst
|
104
|
-
|
120
|
+
processors = types.each_cons(2).map { |src, dst|
|
105
121
|
unless processor = transformers[src][dst]
|
106
122
|
raise ArgumentError, "missing transformer for type: #{src} to #{dst}"
|
107
123
|
end
|
108
|
-
|
109
|
-
|
110
|
-
processors.concat config[:preprocessors][dst]
|
124
|
+
processor
|
125
|
+
}
|
111
126
|
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
if processors.size > 1
|
116
|
-
compose_processors(*processors.reverse)
|
117
|
-
elsif processors.size == 1
|
118
|
-
processors.first
|
119
|
-
end
|
127
|
+
compose_transformer_list processors, preprocessors, postprocessors
|
120
128
|
end
|
121
129
|
|
122
130
|
private
|
123
|
-
def
|
124
|
-
|
125
|
-
|
131
|
+
def compose_transformer_list(transformers, preprocessors, postprocessors)
|
132
|
+
processors = []
|
133
|
+
|
134
|
+
transformers.each do |processor|
|
135
|
+
processors.concat postprocessors[processor.from]
|
136
|
+
processors << processor.proc
|
137
|
+
processors.concat preprocessors[processor.to]
|
138
|
+
end
|
139
|
+
|
140
|
+
if processors.size > 1
|
141
|
+
compose_processors(*processors.reverse)
|
142
|
+
elsif processors.size == 1
|
143
|
+
processors.first
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def compute_transformers!(registered_transformers)
|
148
|
+
preprocessors = self.config[:preprocessors]
|
149
|
+
postprocessors = self.config[:postprocessors]
|
150
|
+
transformers = Hash.new { {} }
|
126
151
|
inverted_transformers = Hash.new { Set.new }
|
152
|
+
incoming_edges = registered_transformers.group_by(&:from)
|
153
|
+
|
154
|
+
registered_transformers.each do |t|
|
155
|
+
traversals = dfs_paths([t]) { |k| incoming_edges.fetch(k.to, []) }
|
127
156
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
src, dst = types.first, types.last
|
132
|
-
processor = compose_transformers(registered_transformers, types)
|
157
|
+
traversals.each do |nodes|
|
158
|
+
src, dst = nodes.first.from, nodes.last.to
|
159
|
+
processor = compose_transformer_list nodes, preprocessors, postprocessors
|
133
160
|
|
134
|
-
|
135
|
-
|
161
|
+
transformers[src] = {} unless transformers.key?(src)
|
162
|
+
transformers[src][dst] = processor
|
136
163
|
|
137
|
-
|
138
|
-
|
164
|
+
inverted_transformers[dst] = Set.new unless inverted_transformers.key?(dst)
|
165
|
+
inverted_transformers[dst] << src
|
166
|
+
end
|
139
167
|
end
|
140
168
|
|
141
169
|
self.config = hash_reassoc(config, :transformers) { transformers }
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/autoload'
|
2
3
|
require 'sprockets/digest_utils'
|
4
|
+
require 'sprockets/source_map_utils'
|
3
5
|
|
4
6
|
module Sprockets
|
5
7
|
# Public: Uglifier/Uglify compressor.
|
@@ -15,7 +17,7 @@ module Sprockets
|
|
15
17
|
# Sprockets::UglifierCompressor.new(comments: :copyright)
|
16
18
|
#
|
17
19
|
class UglifierCompressor
|
18
|
-
VERSION = '
|
20
|
+
VERSION = '3'
|
19
21
|
|
20
22
|
# Public: Return singleton instance with default options.
|
21
23
|
#
|
@@ -35,22 +37,30 @@ module Sprockets
|
|
35
37
|
attr_reader :cache_key
|
36
38
|
|
37
39
|
def initialize(options = {})
|
38
|
-
|
39
|
-
if Autoload::Uglifier::DEFAULTS[:copyright]
|
40
|
-
# Uglifier < 2.x
|
41
|
-
options[:copyright] ||= false
|
42
|
-
else
|
43
|
-
# Uglifier >= 2.x
|
44
|
-
options[:comments] ||= :none
|
45
|
-
end
|
40
|
+
options[:comments] ||= :none
|
46
41
|
|
47
42
|
@options = options
|
48
43
|
@cache_key = "#{self.class.name}:#{Autoload::Uglifier::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
|
49
44
|
end
|
50
45
|
|
51
46
|
def call(input)
|
52
|
-
|
53
|
-
|
47
|
+
case Autoload::Uglifier::VERSION.to_i
|
48
|
+
when 1
|
49
|
+
raise "uglifier 1.x is no longer supported, please upgrade to 2.x or newer"
|
50
|
+
when 2
|
51
|
+
input_options = { source_filename: input[:filename] }
|
52
|
+
else
|
53
|
+
input_options = { source_map: { filename: input[:filename] } }
|
54
|
+
end
|
55
|
+
|
56
|
+
uglifier = Autoload::Uglifier.new(@options.merge(input_options))
|
57
|
+
|
58
|
+
js, map = uglifier.compile_with_map(input[:data])
|
59
|
+
|
60
|
+
map = SourceMapUtils.format_source_map(JSON.parse(map), input)
|
61
|
+
map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
|
62
|
+
|
63
|
+
{ data: js, map: map }
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|