sprockets 3.0.3 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +101 -0
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +531 -276
- data/bin/sprockets +12 -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 +41 -28
- 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 +61 -13
- data/lib/sprockets/bower.rb +6 -3
- data/lib/sprockets/bundle.rb +41 -5
- data/lib/sprockets/cache/file_store.rb +32 -7
- data/lib/sprockets/cache/memory_store.rb +28 -10
- data/lib/sprockets/cache/null_store.rb +8 -0
- data/lib/sprockets/cache.rb +43 -6
- data/lib/sprockets/cached_environment.rb +15 -20
- data/lib/sprockets/closure_compressor.rb +6 -11
- data/lib/sprockets/coffee_script_processor.rb +20 -6
- data/lib/sprockets/compressing.rb +62 -2
- data/lib/sprockets/configuration.rb +5 -9
- data/lib/sprockets/context.rb +99 -25
- data/lib/sprockets/dependencies.rb +10 -9
- data/lib/sprockets/digest_utils.rb +103 -62
- data/lib/sprockets/directive_processor.rb +64 -36
- data/lib/sprockets/eco_processor.rb +4 -3
- data/lib/sprockets/ejs_processor.rb +4 -3
- data/lib/sprockets/encoding_utils.rb +1 -0
- data/lib/sprockets/environment.rb +9 -4
- data/lib/sprockets/erb_processor.rb +34 -21
- 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 +244 -62
- data/lib/sprockets/manifest.rb +100 -46
- data/lib/sprockets/manifest_utils.rb +9 -6
- data/lib/sprockets/mime.rb +8 -42
- 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 +107 -22
- data/lib/sprockets/paths.rb +1 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +32 -52
- data/lib/sprockets/processor_utils.rb +38 -39
- data/lib/sprockets/resolve.rb +177 -97
- data/lib/sprockets/sass_cache_store.rb +1 -0
- data/lib/sprockets/sass_compressor.rb +21 -17
- data/lib/sprockets/sass_functions.rb +1 -0
- data/lib/sprockets/sass_importer.rb +1 -0
- data/lib/sprockets/sass_processor.rb +46 -18
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +77 -44
- 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 +23 -20
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +14 -14
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +63 -71
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +5 -14
- data/lib/sprockets.rb +105 -33
- metadata +157 -27
- data/lib/sprockets/coffee_script_template.rb +0 -6
- data/lib/sprockets/eco_template.rb +0 -6
- data/lib/sprockets/ejs_template.rb +0 -6
- data/lib/sprockets/engines.rb +0 -81
- data/lib/sprockets/erb_template.rb +0 -6
- data/lib/sprockets/legacy.rb +0 -314
- 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 -7
data/lib/sprockets/resolve.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'set'
|
2
3
|
require 'sprockets/http_utils'
|
3
4
|
require 'sprockets/path_dependency_utils'
|
@@ -20,27 +21,21 @@ module Sprockets
|
|
20
21
|
# # => "file:///path/to/app/javascripts/application.coffee?type=application/javascript"
|
21
22
|
#
|
22
23
|
# The String Asset URI is returned or nil if no results are found.
|
23
|
-
def resolve(path,
|
24
|
-
|
25
|
-
paths = options[:load_paths] || config[:paths]
|
26
|
-
accept = options[:accept]
|
24
|
+
def resolve(path, load_paths: config[:paths], accept: nil, pipeline: nil, base_path: nil)
|
25
|
+
paths = load_paths
|
27
26
|
|
28
27
|
if valid_asset_uri?(path)
|
29
28
|
uri, deps = resolve_asset_uri(path)
|
30
29
|
elsif absolute_path?(path)
|
31
30
|
filename, type, deps = resolve_absolute_path(paths, path, accept)
|
32
31
|
elsif relative_path?(path)
|
33
|
-
filename, type,
|
32
|
+
filename, type, path_pipeline, deps, index_alias = resolve_relative_path(paths, path, base_path, accept)
|
34
33
|
else
|
35
|
-
filename, type,
|
34
|
+
filename, type, path_pipeline, deps, index_alias = resolve_logical_path(paths, path, accept)
|
36
35
|
end
|
37
36
|
|
38
37
|
if filename
|
39
|
-
|
40
|
-
params[:type] = type if type
|
41
|
-
params[:pipeline] = pipeline if pipeline
|
42
|
-
params[:pipeline] = options[:pipeline] if options[:pipeline]
|
43
|
-
uri = build_asset_uri(filename, params)
|
38
|
+
uri = build_asset_uri(filename, type: type, pipeline: pipeline || path_pipeline, index_alias: index_alias)
|
44
39
|
end
|
45
40
|
|
46
41
|
return uri, deps
|
@@ -48,18 +43,21 @@ module Sprockets
|
|
48
43
|
|
49
44
|
# Public: Same as resolve() but raises a FileNotFound exception instead of
|
50
45
|
# nil if no assets are found.
|
51
|
-
def resolve!(path,
|
52
|
-
uri, deps = resolve(path,
|
46
|
+
def resolve!(path, **kargs)
|
47
|
+
uri, deps = resolve(path, **kargs)
|
53
48
|
|
54
49
|
unless uri
|
55
|
-
message = "couldn't find file '#{path}'"
|
50
|
+
message = +"couldn't find file '#{path}'"
|
56
51
|
|
57
|
-
if relative_path?(path) &&
|
58
|
-
load_path, _ = paths_split(config[:paths],
|
52
|
+
if relative_path?(path) && kargs[:base_path]
|
53
|
+
load_path, _ = paths_split(config[:paths], kargs[:base_path])
|
59
54
|
message << " under '#{load_path}'"
|
60
55
|
end
|
61
56
|
|
62
|
-
message << " with type '#{
|
57
|
+
message << " with type '#{kargs[:accept]}'" if kargs[:accept]
|
58
|
+
|
59
|
+
load_paths = kargs[:load_paths] || config[:paths]
|
60
|
+
message << "\nChecked in these paths: \n #{ load_paths.join("\n ") }"
|
63
61
|
|
64
62
|
raise FileNotFound, message
|
65
63
|
end
|
@@ -68,148 +66,230 @@ module Sprockets
|
|
68
66
|
end
|
69
67
|
|
70
68
|
protected
|
69
|
+
|
70
|
+
# Internal: Finds an asset given a URI
|
71
|
+
#
|
72
|
+
# uri - String. Contains file:// scheme, absolute path to
|
73
|
+
# file.
|
74
|
+
# e.g. "file:///Users/schneems/sprockets/test/fixtures/default/gallery.js?type=application/javascript"
|
75
|
+
#
|
76
|
+
# Returns Array. Contains a String uri and Set of dependencies
|
71
77
|
def resolve_asset_uri(uri)
|
72
|
-
filename, _ = parse_asset_uri(uri)
|
73
|
-
return uri, Set.new([build_file_digest_uri(filename)])
|
78
|
+
filename, _ = URIUtils.parse_asset_uri(uri)
|
79
|
+
return uri, Set.new( [URIUtils.build_file_digest_uri(filename)] )
|
74
80
|
end
|
75
81
|
|
82
|
+
# Internal: Finds a file in a set of given paths
|
83
|
+
#
|
84
|
+
# paths - Array of Strings.
|
85
|
+
# filename - String containing absolute path to a file including extension.
|
86
|
+
# e.g. "/Users/schneems/sprockets/test/fixtures/asset/application.js"
|
87
|
+
# accept - String. A Quality value incoded set of
|
88
|
+
# mime types that we are looking for. Can be nil.
|
89
|
+
# e.g. "application/javascript" or "text/css, */*"
|
90
|
+
#
|
91
|
+
# Returns Array. Filename, type, path_pipeline, deps, index_alias
|
76
92
|
def resolve_absolute_path(paths, filename, accept)
|
77
93
|
deps = Set.new
|
78
94
|
filename = File.expand_path(filename)
|
79
95
|
|
80
96
|
# Ensure path is under load paths
|
81
|
-
return nil, nil, deps unless paths_split(paths, filename)
|
97
|
+
return nil, nil, deps unless PathUtils.paths_split(paths, filename)
|
82
98
|
|
83
|
-
_, mime_type
|
99
|
+
_, mime_type = PathUtils.match_path_extname(filename, config[:mime_exts])
|
84
100
|
type = resolve_transform_type(mime_type, accept)
|
85
101
|
return nil, nil, deps if accept && !type
|
86
102
|
|
87
103
|
return nil, nil, deps unless file?(filename)
|
88
104
|
|
89
|
-
deps << build_file_digest_uri(filename)
|
105
|
+
deps << URIUtils.build_file_digest_uri(filename)
|
90
106
|
return filename, type, deps
|
91
107
|
end
|
92
108
|
|
109
|
+
# Internal: Finds a relative file in a set of given paths
|
110
|
+
#
|
111
|
+
# paths - Array of Strings.
|
112
|
+
# path - String. A relative filename with or without extension
|
113
|
+
# e.g. "./jquery" or "../foo.js"
|
114
|
+
# dirname - String. Base path where we start looking for the given file.
|
115
|
+
# accept - String. A Quality value incoded set of
|
116
|
+
# mime types that we are looking for. Can be nil.
|
117
|
+
# e.g. "application/javascript" or "text/css, */*"
|
118
|
+
#
|
119
|
+
# Returns Array. Filename, type, path_pipeline, deps, index_alias
|
93
120
|
def resolve_relative_path(paths, path, dirname, accept)
|
94
121
|
filename = File.expand_path(path, dirname)
|
95
|
-
load_path, _ = paths_split(paths, dirname)
|
96
|
-
if load_path && logical_path = split_subpath(load_path, filename)
|
122
|
+
load_path, _ = PathUtils.paths_split(paths, dirname)
|
123
|
+
if load_path && logical_path = PathUtils.split_subpath(load_path, filename)
|
97
124
|
resolve_logical_path([load_path], logical_path, accept)
|
98
125
|
else
|
99
|
-
return nil, nil, Set.new
|
126
|
+
return nil, nil, nil, Set.new
|
100
127
|
end
|
101
128
|
end
|
102
129
|
|
130
|
+
# Internal: Finds a file in a set of given paths
|
131
|
+
#
|
132
|
+
# paths - Array of Strings.
|
133
|
+
# logical_path - String. A filename with extension
|
134
|
+
# e.g. "coffee/foo.js" or "foo.js"
|
135
|
+
# accept - String. A Quality value incoded set of
|
136
|
+
# mime types that we are looking for. Can be nil.
|
137
|
+
# e.g. "application/javascript" or "text/css, */*"
|
138
|
+
#
|
139
|
+
# Finds a file on the given paths.
|
140
|
+
#
|
141
|
+
# Returns Array. Filename, type, path_pipeline, deps, index_alias
|
103
142
|
def resolve_logical_path(paths, logical_path, accept)
|
104
|
-
|
143
|
+
extname, mime_type = PathUtils.match_path_extname(logical_path, config[:mime_exts])
|
144
|
+
logical_name = logical_path.chomp(extname)
|
145
|
+
|
146
|
+
extname, pipeline = PathUtils.match_path_extname(logical_name, config[:pipeline_exts])
|
147
|
+
logical_name = logical_name.chomp(extname)
|
148
|
+
|
105
149
|
parsed_accept = parse_accept_options(mime_type, accept)
|
106
150
|
transformed_accepts = expand_transform_accepts(parsed_accept)
|
107
|
-
|
151
|
+
|
152
|
+
filename, mime_type, deps, index_alias = resolve_under_paths(paths, logical_name, transformed_accepts)
|
108
153
|
|
109
154
|
if filename
|
110
155
|
deps << build_file_digest_uri(filename)
|
111
156
|
type = resolve_transform_type(mime_type, parsed_accept)
|
112
|
-
return filename, type, pipeline, deps
|
157
|
+
return filename, type, pipeline, deps, index_alias
|
113
158
|
else
|
114
159
|
return nil, nil, nil, deps
|
115
160
|
end
|
116
161
|
end
|
117
162
|
|
163
|
+
# Internal: Finds a file in a set of given paths
|
164
|
+
#
|
165
|
+
# paths - Array of Strings.
|
166
|
+
# logical_name - String. A filename without extension
|
167
|
+
# e.g. "application" or "coffee/foo"
|
168
|
+
# accepts - Array of array containing mime/version pairs
|
169
|
+
# e.g. [["application/javascript", 1.0]]
|
170
|
+
#
|
171
|
+
# Finds a file with the same name as `logical_name` or "index" inside
|
172
|
+
# of the `logical_name` directory that matches a valid mime-type/version from
|
173
|
+
# `accepts`.
|
174
|
+
#
|
175
|
+
# Returns Array. Filename, type, dependencies, and index_alias
|
118
176
|
def resolve_under_paths(paths, logical_name, accepts)
|
119
|
-
|
120
|
-
return nil, nil,
|
177
|
+
deps = Set.new
|
178
|
+
return nil, nil, deps if accepts.empty?
|
179
|
+
|
180
|
+
# TODO: Allow new path resolves to be registered
|
181
|
+
@resolvers ||= [
|
182
|
+
method(:resolve_main_under_path),
|
183
|
+
method(:resolve_alts_under_path),
|
184
|
+
method(:resolve_index_under_path)
|
185
|
+
]
|
186
|
+
mime_exts = config[:mime_exts]
|
121
187
|
|
122
|
-
logical_basename = File.basename(logical_name)
|
123
188
|
paths.each do |load_path|
|
124
|
-
candidates
|
125
|
-
|
126
|
-
|
127
|
-
|
189
|
+
candidates = []
|
190
|
+
@resolvers.each do |fn|
|
191
|
+
result = fn.call(load_path, logical_name, mime_exts)
|
192
|
+
candidates.concat(result[0])
|
193
|
+
deps.merge(result[1])
|
128
194
|
end
|
129
|
-
return candidate + [all_deps] if candidate
|
130
|
-
end
|
131
195
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
def parse_accept_options(mime_type, types)
|
136
|
-
accepts = []
|
137
|
-
accepts += parse_q_values(types) if types
|
138
|
-
|
139
|
-
if mime_type
|
140
|
-
if accepts.empty? || accepts.any? { |accept, _| match_mime_type?(mime_type, accept) }
|
141
|
-
accepts = [[mime_type, 1.0]]
|
142
|
-
else
|
143
|
-
return []
|
196
|
+
candidate = HTTPUtils.find_best_q_match(accepts, candidates) do |c, matcher|
|
197
|
+
match_mime_type?(c[:type] || "application/octet-stream", matcher)
|
144
198
|
end
|
199
|
+
return candidate[:filename], candidate[:type], deps, candidate[:index_alias] if candidate
|
145
200
|
end
|
146
201
|
|
147
|
-
|
148
|
-
accepts << ['*/*', 1.0]
|
149
|
-
end
|
150
|
-
|
151
|
-
accepts
|
202
|
+
return nil, nil, deps
|
152
203
|
end
|
153
204
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
205
|
+
# Internal: Finds candidate files on a given path
|
206
|
+
#
|
207
|
+
# load_path - String. An absolute path to a directory
|
208
|
+
# logical_name - String. A filename without extension
|
209
|
+
# e.g. "application" or "coffee/foo"
|
210
|
+
# mime_exts - Hash of file extensions and their mime types
|
211
|
+
# e.g. {".xml.builder"=>"application/xml+builder"}
|
212
|
+
#
|
213
|
+
# Finds files that match a given `logical_name` with an acceptable
|
214
|
+
# mime type that is included in `mime_exts` on the `load_path`.
|
215
|
+
#
|
216
|
+
# Returns Array. First element is an Array of hashes or empty, second is a String
|
217
|
+
def resolve_main_under_path(load_path, logical_name, mime_exts)
|
161
218
|
dirname = File.dirname(File.join(load_path, logical_name))
|
162
|
-
candidates =
|
163
|
-
|
164
|
-
|
165
|
-
result = resolve_alternates(load_path, logical_name)
|
166
|
-
result[0].each do |fn|
|
167
|
-
candidates << [fn, parse_path_extnames(fn)[1]]
|
219
|
+
candidates = self.find_matching_path_for_extensions(dirname, File.basename(logical_name), mime_exts)
|
220
|
+
candidates.map! do |c|
|
221
|
+
{ filename: c[0], type: c[1] }
|
168
222
|
end
|
169
|
-
|
223
|
+
return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
|
224
|
+
end
|
225
|
+
|
170
226
|
|
227
|
+
# Internal: Finds candidate index files in a given path
|
228
|
+
#
|
229
|
+
# load_path - String. An absolute path to a directory
|
230
|
+
# logical_name - String. A filename without extension
|
231
|
+
# e.g. "application" or "coffee/foo"
|
232
|
+
# mime_exts - Hash of file extensions and their mime types
|
233
|
+
# e.g. {".xml.builder"=>"application/xml+builder"}
|
234
|
+
#
|
235
|
+
# Looking in the given `load_path` this method will find all files under the `logical_name` directory
|
236
|
+
# that are named `index` and have a matching mime type in `mime_exts`.
|
237
|
+
#
|
238
|
+
# Returns Array. First element is an Array of hashes or empty, second is a String
|
239
|
+
def resolve_index_under_path(load_path, logical_name, mime_exts)
|
171
240
|
dirname = File.join(load_path, logical_name)
|
172
|
-
|
173
|
-
|
174
|
-
candidates.
|
241
|
+
|
242
|
+
if self.directory?(dirname)
|
243
|
+
candidates = self.find_matching_path_for_extensions(dirname, "index".freeze, mime_exts)
|
244
|
+
else
|
245
|
+
candidates = []
|
175
246
|
end
|
176
247
|
|
177
|
-
|
248
|
+
candidates.map! do |c|
|
249
|
+
{ filename: c[0],
|
250
|
+
type: c[1],
|
251
|
+
index_alias: compress_from_root(c[0].sub(/\/index(\.[^\/]+)$/, '\1')) }
|
252
|
+
end
|
178
253
|
|
179
|
-
return candidates
|
254
|
+
return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
|
180
255
|
end
|
181
256
|
|
182
|
-
def
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
if basename == name
|
188
|
-
candidates << [File.join(dirname, entry), type]
|
189
|
-
end
|
257
|
+
def resolve_alts_under_path(load_path, logical_name, mime_exts)
|
258
|
+
filenames, deps = self.resolve_alternates(load_path, logical_name)
|
259
|
+
filenames.map! do |fn|
|
260
|
+
_, mime_type = PathUtils.match_path_extname(fn, mime_exts)
|
261
|
+
{ filename: fn, type: mime_type }
|
190
262
|
end
|
191
|
-
|
263
|
+
return filenames, deps
|
192
264
|
end
|
193
265
|
|
194
|
-
|
195
|
-
return [], Set.new
|
196
|
-
end
|
197
|
-
|
198
|
-
# Internal: Returns the name, mime type and `Array` of engine extensions.
|
266
|
+
# Internal: Converts mimetype into accept Array
|
199
267
|
#
|
200
|
-
#
|
201
|
-
#
|
268
|
+
# - mime_type - String, optional. e.g. "text/html"
|
269
|
+
# - explicit_type - String, optional. e.g. "application/javascript"
|
202
270
|
#
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
271
|
+
# When called with an explicit_type and a mime_type, only a mime_type
|
272
|
+
# that matches the given explicit_type will be accepted.
|
273
|
+
#
|
274
|
+
# Returns Array of Array
|
275
|
+
#
|
276
|
+
# [["application/javascript", 1.0]]
|
277
|
+
# [["*/*", 1.0]]
|
278
|
+
# []
|
279
|
+
def parse_accept_options(mime_type, explicit_type)
|
280
|
+
if mime_type
|
281
|
+
return [[mime_type, 1.0]] if explicit_type.nil?
|
282
|
+
return [[mime_type, 1.0]] if HTTPUtils.parse_q_values(explicit_type).any? { |accept, _| HTTPUtils.match_mime_type?(mime_type, accept) }
|
283
|
+
return []
|
210
284
|
end
|
211
285
|
|
212
|
-
|
286
|
+
accepts = HTTPUtils.parse_q_values(explicit_type)
|
287
|
+
accepts << ['*/*'.freeze, 1.0] if accepts.empty?
|
288
|
+
return accepts
|
289
|
+
end
|
290
|
+
|
291
|
+
def resolve_alternates(load_path, logical_name)
|
292
|
+
return [], Set.new
|
213
293
|
end
|
214
294
|
end
|
215
295
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/autoload'
|
3
|
+
require 'sprockets/digest_utils'
|
4
|
+
require 'sprockets/source_map_utils'
|
2
5
|
|
3
6
|
module Sprockets
|
4
7
|
# Public: Sass CSS minifier.
|
@@ -34,26 +37,27 @@ module Sprockets
|
|
34
37
|
attr_reader :cache_key
|
35
38
|
|
36
39
|
def initialize(options = {})
|
37
|
-
@options =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
@options = {
|
41
|
+
syntax: :scss,
|
42
|
+
cache: false,
|
43
|
+
read_cache: false,
|
44
|
+
style: :compressed
|
45
|
+
}.merge(options).freeze
|
46
|
+
@cache_key = "#{self.class.name}:#{Autoload::Sass::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
|
44
47
|
end
|
45
48
|
|
46
49
|
def call(input)
|
47
|
-
|
48
|
-
|
49
|
-
options
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
css, map = Autoload::Sass::Engine.new(
|
51
|
+
input[:data],
|
52
|
+
@options.merge(filename: input[:filename])
|
53
|
+
).render_with_sourcemap('')
|
54
|
+
|
55
|
+
css = css.sub("/*# sourceMappingURL= */\n", '')
|
56
|
+
|
57
|
+
map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input)
|
58
|
+
map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
|
59
|
+
|
60
|
+
{ data: css, map: map }
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'rack/utils'
|
2
3
|
require 'sprockets/autoload'
|
4
|
+
require 'sprockets/source_map_utils'
|
3
5
|
require 'uri'
|
4
6
|
|
5
7
|
module Sprockets
|
6
8
|
# Processor engine class for the SASS/SCSS compiler. Depends on the `sass` gem.
|
7
9
|
#
|
8
|
-
# For more
|
10
|
+
# For more information see:
|
9
11
|
#
|
10
12
|
# https://github.com/sass/sass
|
11
13
|
# https://github.com/rails/sass-rails
|
@@ -39,18 +41,14 @@ module Sprockets
|
|
39
41
|
# Public: Initialize template with custom options.
|
40
42
|
#
|
41
43
|
# options - Hash
|
42
|
-
#
|
43
|
-
#
|
44
|
+
# cache_version - String custom cache version. Used to force a cache
|
45
|
+
# change after code changes are made to Sass Functions.
|
44
46
|
#
|
45
47
|
def initialize(options = {}, &block)
|
46
48
|
@cache_version = options[:cache_version]
|
47
|
-
@cache_key =
|
48
|
-
|
49
|
-
|
50
|
-
Autoload::Sass::VERSION,
|
51
|
-
@cache_version
|
52
|
-
].freeze
|
53
|
-
|
49
|
+
@cache_key = "#{self.class.name}:#{VERSION}:#{Autoload::Sass::VERSION}:#{@cache_version}".freeze
|
50
|
+
@importer_class = options[:importer] || Sass::Importers::Filesystem
|
51
|
+
@sass_config = options[:sass_config] || {}
|
54
52
|
@functions = Module.new do
|
55
53
|
include Functions
|
56
54
|
include options[:functions] if options[:functions]
|
@@ -61,24 +59,30 @@ module Sprockets
|
|
61
59
|
def call(input)
|
62
60
|
context = input[:environment].context_class.new(input)
|
63
61
|
|
64
|
-
|
62
|
+
engine_options = merge_options({
|
65
63
|
filename: input[:filename],
|
66
64
|
syntax: self.class.syntax,
|
67
|
-
cache_store:
|
68
|
-
load_paths:
|
65
|
+
cache_store: build_cache_store(input, @cache_version),
|
66
|
+
load_paths: context.environment.paths.map { |p| @importer_class.new(p.to_s) },
|
67
|
+
importer: @importer_class.new(Pathname.new(context.filename).to_s),
|
69
68
|
sprockets: {
|
70
69
|
context: context,
|
71
70
|
environment: input[:environment],
|
72
71
|
dependencies: context.metadata[:dependencies]
|
73
72
|
}
|
74
|
-
}
|
73
|
+
})
|
75
74
|
|
76
|
-
engine = Autoload::Sass::Engine.new(input[:data],
|
75
|
+
engine = Autoload::Sass::Engine.new(input[:data], engine_options)
|
77
76
|
|
78
|
-
css = Utils.module_include(Autoload::Sass::Script::Functions, @functions) do
|
79
|
-
engine.
|
77
|
+
css, map = Utils.module_include(Autoload::Sass::Script::Functions, @functions) do
|
78
|
+
engine.render_with_sourcemap('')
|
80
79
|
end
|
81
80
|
|
81
|
+
css = css.sub("\n/*# sourceMappingURL= */\n", '')
|
82
|
+
|
83
|
+
map = SourceMapUtils.format_source_map(JSON.parse(map.to_json(css_uri: '')), input)
|
84
|
+
map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
|
85
|
+
|
82
86
|
# Track all imported files
|
83
87
|
sass_dependencies = Set.new([input[:filename]])
|
84
88
|
engine.dependencies.map do |dependency|
|
@@ -86,7 +90,31 @@ module Sprockets
|
|
86
90
|
context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.options[:filename])
|
87
91
|
end
|
88
92
|
|
89
|
-
context.metadata.merge(data: css, sass_dependencies: sass_dependencies)
|
93
|
+
context.metadata.merge(data: css, sass_dependencies: sass_dependencies, map: map)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
# Public: Build the cache store to be used by the Sass engine.
|
99
|
+
#
|
100
|
+
# input - the input hash.
|
101
|
+
# version - the cache version.
|
102
|
+
#
|
103
|
+
# Override this method if you need to use a different cache than the
|
104
|
+
# Sprockets cache.
|
105
|
+
def build_cache_store(input, version)
|
106
|
+
CacheStore.new(input[:cache], version)
|
107
|
+
end
|
108
|
+
|
109
|
+
def merge_options(options)
|
110
|
+
defaults = @sass_config.dup
|
111
|
+
|
112
|
+
if load_paths = defaults.delete(:load_paths)
|
113
|
+
options[:load_paths] += load_paths
|
114
|
+
end
|
115
|
+
|
116
|
+
options.merge!(defaults)
|
117
|
+
options
|
90
118
|
end
|
91
119
|
|
92
120
|
# Public: Functions injected into Sass context during Sprockets evaluation.
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'sprockets/autoload'
|
3
|
+
require 'sprockets/source_map_utils'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
# Public: Sass CSS minifier.
|
7
|
+
#
|
8
|
+
# To accept the default options
|
9
|
+
#
|
10
|
+
# environment.register_bundle_processor 'text/css',
|
11
|
+
# Sprockets::SasscCompressor
|
12
|
+
#
|
13
|
+
# Or to pass options to the Sass::Engine class.
|
14
|
+
#
|
15
|
+
# environment.register_bundle_processor 'text/css',
|
16
|
+
# Sprockets::SasscCompressor.new({ ... })
|
17
|
+
#
|
18
|
+
class SasscCompressor
|
19
|
+
# Public: Return singleton instance with default options.
|
20
|
+
#
|
21
|
+
# Returns SasscCompressor object.
|
22
|
+
def self.instance
|
23
|
+
@instance ||= new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.call(input)
|
27
|
+
instance.call(input)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(options = {})
|
31
|
+
@options = {
|
32
|
+
syntax: :scss,
|
33
|
+
style: :compressed,
|
34
|
+
source_map_contents: false,
|
35
|
+
omit_source_map_url: true,
|
36
|
+
}.merge(options).freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
def call(input)
|
40
|
+
# SassC requires the template to be modifiable
|
41
|
+
input_data = input[:data].frozen? ? input[:data].dup : input[:data]
|
42
|
+
engine = Autoload::SassC::Engine.new(input_data, @options.merge(filename: input[:filename], source_map_file: "#{input[:filename]}.map"))
|
43
|
+
|
44
|
+
css = engine.render.sub(/^\n^\/\*# sourceMappingURL=.*\*\/$/m, '')
|
45
|
+
|
46
|
+
begin
|
47
|
+
map = SourceMapUtils.format_source_map(JSON.parse(engine.source_map), input)
|
48
|
+
map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
|
49
|
+
rescue SassC::NotRenderedError
|
50
|
+
map = input[:metadata][:map]
|
51
|
+
end
|
52
|
+
|
53
|
+
{ data: css, map: map }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|