sprockets 3.7.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -270
  3. data/README.md +443 -320
  4. data/bin/sprockets +11 -7
  5. data/lib/rake/sprocketstask.rb +3 -2
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +16 -21
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +1 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  11. data/lib/sprockets/autoload/eco.rb +1 -0
  12. data/lib/sprockets/autoload/ejs.rb +1 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +1 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +1 -0
  17. data/lib/sprockets/autoload/yui.rb +1 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +5 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +47 -10
  22. data/lib/sprockets/bower.rb +5 -2
  23. data/lib/sprockets/bundle.rb +40 -4
  24. data/lib/sprockets/cache/file_store.rb +25 -3
  25. data/lib/sprockets/cache/memory_store.rb +9 -0
  26. data/lib/sprockets/cache/null_store.rb +8 -0
  27. data/lib/sprockets/cache.rb +36 -1
  28. data/lib/sprockets/cached_environment.rb +14 -19
  29. data/lib/sprockets/closure_compressor.rb +1 -0
  30. data/lib/sprockets/coffee_script_processor.rb +18 -4
  31. data/lib/sprockets/compressing.rb +43 -3
  32. data/lib/sprockets/configuration.rb +3 -7
  33. data/lib/sprockets/context.rb +97 -24
  34. data/lib/sprockets/dependencies.rb +1 -0
  35. data/lib/sprockets/digest_utils.rb +25 -5
  36. data/lib/sprockets/directive_processor.rb +45 -35
  37. data/lib/sprockets/eco_processor.rb +1 -0
  38. data/lib/sprockets/ejs_processor.rb +1 -0
  39. data/lib/sprockets/encoding_utils.rb +1 -0
  40. data/lib/sprockets/environment.rb +9 -4
  41. data/lib/sprockets/erb_processor.rb +28 -21
  42. data/lib/sprockets/errors.rb +1 -0
  43. data/lib/sprockets/exporters/base.rb +72 -0
  44. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  45. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  46. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  47. data/lib/sprockets/exporting.rb +73 -0
  48. data/lib/sprockets/file_reader.rb +1 -0
  49. data/lib/sprockets/http_utils.rb +25 -7
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +11 -10
  52. data/lib/sprockets/loader.rb +85 -67
  53. data/lib/sprockets/manifest.rb +64 -62
  54. data/lib/sprockets/manifest_utils.rb +9 -6
  55. data/lib/sprockets/mime.rb +8 -42
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +3 -11
  58. data/lib/sprockets/path_digest_utils.rb +2 -1
  59. data/lib/sprockets/path_utils.rb +87 -7
  60. data/lib/sprockets/paths.rb +1 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +31 -61
  63. data/lib/sprockets/processor_utils.rb +24 -35
  64. data/lib/sprockets/resolve.rb +177 -93
  65. data/lib/sprockets/sass_cache_store.rb +2 -6
  66. data/lib/sprockets/sass_compressor.rb +13 -1
  67. data/lib/sprockets/sass_functions.rb +1 -0
  68. data/lib/sprockets/sass_importer.rb +1 -0
  69. data/lib/sprockets/sass_processor.rb +30 -9
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +26 -23
  73. data/lib/sprockets/source_map_processor.rb +66 -0
  74. data/lib/sprockets/source_map_utils.rb +483 -0
  75. data/lib/sprockets/transformers.rb +63 -35
  76. data/lib/sprockets/uglifier_compressor.rb +21 -11
  77. data/lib/sprockets/unloaded_asset.rb +13 -11
  78. data/lib/sprockets/uri_tar.rb +1 -0
  79. data/lib/sprockets/uri_utils.rb +11 -8
  80. data/lib/sprockets/utils/gzip.rb +46 -14
  81. data/lib/sprockets/utils.rb +41 -74
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +1 -0
  84. data/lib/sprockets.rb +99 -39
  85. metadata +127 -23
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/coffee_script_template.rb +0 -17
  88. data/lib/sprockets/deprecation.rb +0 -90
  89. data/lib/sprockets/eco_template.rb +0 -17
  90. data/lib/sprockets/ejs_template.rb +0 -17
  91. data/lib/sprockets/engines.rb +0 -92
  92. data/lib/sprockets/erb_template.rb +0 -11
  93. data/lib/sprockets/legacy.rb +0 -330
  94. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  95. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  96. data/lib/sprockets/sass_template.rb +0 -19
@@ -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, options = {})
24
- path = path.to_s
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, pipeline, deps = resolve_relative_path(paths, path, options[:base_path], accept)
32
+ filename, type, path_pipeline, deps, index_alias = resolve_relative_path(paths, path, base_path, accept)
34
33
  else
35
- filename, type, pipeline, deps = resolve_logical_path(paths, path, accept)
34
+ filename, type, path_pipeline, deps, index_alias = resolve_logical_path(paths, path, accept)
36
35
  end
37
36
 
38
37
  if filename
39
- params = {}
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,19 +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, options = {})
52
- uri, deps = resolve(path, options.merge(compat: false))
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) && options[:base_path]
58
- load_path, _ = paths_split(config[:paths], options[:base_path])
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 '#{options[:accept]}'" if options[:accept]
63
- message << "\nChecked in these paths: \n #{ config[:paths].join("\n ") }"
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 ") }"
64
61
 
65
62
  raise FileNotFound, message
66
63
  end
@@ -69,143 +66,230 @@ module Sprockets
69
66
  end
70
67
 
71
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
72
77
  def resolve_asset_uri(uri)
73
- filename, _ = parse_asset_uri(uri)
74
- 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)] )
75
80
  end
76
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
77
92
  def resolve_absolute_path(paths, filename, accept)
78
93
  deps = Set.new
79
94
  filename = File.expand_path(filename)
80
95
 
81
96
  # Ensure path is under load paths
82
- return nil, nil, deps unless paths_split(paths, filename)
97
+ return nil, nil, deps unless PathUtils.paths_split(paths, filename)
83
98
 
84
- _, mime_type, _, _ = parse_path_extnames(filename)
99
+ _, mime_type = PathUtils.match_path_extname(filename, config[:mime_exts])
85
100
  type = resolve_transform_type(mime_type, accept)
86
101
  return nil, nil, deps if accept && !type
87
102
 
88
103
  return nil, nil, deps unless file?(filename)
89
104
 
90
- deps << build_file_digest_uri(filename)
105
+ deps << URIUtils.build_file_digest_uri(filename)
91
106
  return filename, type, deps
92
107
  end
93
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
94
120
  def resolve_relative_path(paths, path, dirname, accept)
95
121
  filename = File.expand_path(path, dirname)
96
- load_path, _ = paths_split(paths, dirname)
97
- 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)
98
124
  resolve_logical_path([load_path], logical_path, accept)
99
125
  else
100
- return nil, nil, Set.new
126
+ return nil, nil, nil, Set.new
101
127
  end
102
128
  end
103
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
104
142
  def resolve_logical_path(paths, logical_path, accept)
105
- logical_name, mime_type, _, pipeline = parse_path_extnames(logical_path)
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
+
106
149
  parsed_accept = parse_accept_options(mime_type, accept)
107
150
  transformed_accepts = expand_transform_accepts(parsed_accept)
108
- filename, mime_type, deps = resolve_under_paths(paths, logical_name, transformed_accepts)
151
+
152
+ filename, mime_type, deps, index_alias = resolve_under_paths(paths, logical_name, transformed_accepts)
109
153
 
110
154
  if filename
111
155
  deps << build_file_digest_uri(filename)
112
156
  type = resolve_transform_type(mime_type, parsed_accept)
113
- return filename, type, pipeline, deps
157
+ return filename, type, pipeline, deps, index_alias
114
158
  else
115
159
  return nil, nil, nil, deps
116
160
  end
117
161
  end
118
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
119
176
  def resolve_under_paths(paths, logical_name, accepts)
120
- all_deps = Set.new
121
- return nil, nil, all_deps if accepts.empty?
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]
122
187
 
123
- logical_basename = File.basename(logical_name)
124
188
  paths.each do |load_path|
125
- candidates, deps = path_matches(load_path, logical_name, logical_basename)
126
- all_deps.merge(deps)
127
- candidate = find_best_q_match(accepts, candidates) do |c, matcher|
128
- match_mime_type?(c[1] || "application/octet-stream", matcher)
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])
129
194
  end
130
- return candidate + [all_deps] if candidate
131
- end
132
195
 
133
- return nil, nil, all_deps
134
- end
135
-
136
- def parse_accept_options(mime_type, types)
137
- accepts = []
138
- accepts += parse_q_values(types) if types
139
-
140
- if mime_type
141
- if accepts.empty? || accepts.any? { |accept, _| match_mime_type?(mime_type, accept) }
142
- accepts = [[mime_type, 1.0]]
143
- else
144
- return []
196
+ candidate = HTTPUtils.find_best_q_match(accepts, candidates) do |c, matcher|
197
+ match_mime_type?(c[:type] || "application/octet-stream", matcher)
145
198
  end
199
+ return candidate[:filename], candidate[:type], deps, candidate[:index_alias] if candidate
146
200
  end
147
201
 
148
- if accepts.empty?
149
- accepts << ['*/*', 1.0]
150
- end
151
-
152
- accepts
202
+ return nil, nil, deps
153
203
  end
154
204
 
155
- def path_matches(load_path, logical_name, logical_basename)
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)
156
218
  dirname = File.dirname(File.join(load_path, logical_name))
157
- candidates = dirname_matches(dirname, logical_basename)
158
- deps = file_digest_dependency_set(dirname)
159
-
160
- result = resolve_alternates(load_path, logical_name)
161
- result[0].each do |fn|
162
- 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] }
163
222
  end
164
- deps.merge(result[1])
223
+ return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
224
+ end
225
+
165
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)
166
240
  dirname = File.join(load_path, logical_name)
167
- if directory? dirname
168
- result = dirname_matches(dirname, "index")
169
- candidates.concat(result)
241
+
242
+ if self.directory?(dirname)
243
+ candidates = self.find_matching_path_for_extensions(dirname, "index".freeze, mime_exts)
244
+ else
245
+ candidates = []
170
246
  end
171
247
 
172
- deps.merge(file_digest_dependency_set(dirname))
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
173
253
 
174
- return candidates.select { |fn, _| file?(fn) }, deps
254
+ return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
175
255
  end
176
256
 
177
- def dirname_matches(dirname, basename)
178
- candidates = []
179
- entries = self.entries(dirname)
180
- entries.each do |entry|
181
- next unless File.basename(entry).start_with?(basename)
182
- name, type, _, _ = parse_path_extnames(entry)
183
- if basename == name
184
- candidates << [File.join(dirname, entry), type]
185
- 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 }
186
262
  end
187
- candidates
263
+ return filenames, deps
188
264
  end
189
265
 
190
- def resolve_alternates(load_path, logical_name)
191
- return [], Set.new
192
- end
193
-
194
- # Internal: Returns the name, mime type and `Array` of engine extensions.
266
+ # Internal: Converts mimetype into accept Array
195
267
  #
196
- # "foo.js.coffee.erb"
197
- # # => ["foo", "application/javascript", [".coffee", ".erb"]]
268
+ # - mime_type - String, optional. e.g. "text/html"
269
+ # - explicit_type - String, optional. e.g. "application/javascript"
198
270
  #
199
- def parse_path_extnames(path)
200
- engines = []
201
- extname, value = match_path_extname(path, extname_map)
202
-
203
- if extname
204
- path = path.chomp(extname)
205
- type, engines, pipeline = value.values_at(:type, :engines, :pipeline)
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 []
206
284
  end
207
285
 
208
- return path, type, engines, pipeline
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
209
293
  end
210
294
  end
211
295
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sass'
2
3
 
3
4
  module Sprockets
@@ -25,10 +26,5 @@ module Sprockets
25
26
  end
26
27
 
27
28
  # Deprecated: Use Sprockets::SassProcessor::CacheStore instead.
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
+ SassCacheStore = SassProcessor::CacheStore
34
30
  end
@@ -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: Sass CSS minifier.
@@ -45,7 +47,17 @@ module Sprockets
45
47
  end
46
48
 
47
49
  def call(input)
48
- Autoload::Sass::Engine.new(input[:data], @options).render
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 }
49
61
  end
50
62
  end
51
63
  end
@@ -1,2 +1,3 @@
1
+ # frozen_string_literal: true
1
2
  # Deprecated: Require sprockets/sass_processor instead
2
3
  require 'sprockets/sass_processor'
@@ -1,2 +1,3 @@
1
+ # frozen_string_literal: true
1
2
  # Deprecated: Require sprockets/sass_processor instead
2
3
  require 'sprockets/sass_processor'
@@ -1,5 +1,7 @@
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
@@ -45,7 +47,8 @@ module Sprockets
45
47
  def initialize(options = {}, &block)
46
48
  @cache_version = options[:cache_version]
47
49
  @cache_key = "#{self.class.name}:#{VERSION}:#{Autoload::Sass::VERSION}:#{@cache_version}".freeze
48
-
50
+ @importer_class = options[:importer] || Sass::Importers::Filesystem
51
+ @sass_config = options[:sass_config] || {}
49
52
  @functions = Module.new do
50
53
  include Functions
51
54
  include options[:functions] if options[:functions]
@@ -56,24 +59,30 @@ module Sprockets
56
59
  def call(input)
57
60
  context = input[:environment].context_class.new(input)
58
61
 
59
- options = {
62
+ engine_options = merge_options({
60
63
  filename: input[:filename],
61
64
  syntax: self.class.syntax,
62
65
  cache_store: build_cache_store(input, @cache_version),
63
- load_paths: input[:environment].paths,
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),
64
68
  sprockets: {
65
69
  context: context,
66
70
  environment: input[:environment],
67
71
  dependencies: context.metadata[:dependencies]
68
72
  }
69
- }
73
+ })
70
74
 
71
- engine = Autoload::Sass::Engine.new(input[:data], options)
75
+ engine = Autoload::Sass::Engine.new(input[:data], engine_options)
72
76
 
73
- css = Utils.module_include(Autoload::Sass::Script::Functions, @functions) do
74
- engine.render
77
+ css, map = Utils.module_include(Autoload::Sass::Script::Functions, @functions) do
78
+ engine.render_with_sourcemap('')
75
79
  end
76
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
+
77
86
  # Track all imported files
78
87
  sass_dependencies = Set.new([input[:filename]])
79
88
  engine.dependencies.map do |dependency|
@@ -81,9 +90,11 @@ module Sprockets
81
90
  context.metadata[:dependencies] << URIUtils.build_file_digest_uri(dependency.options[:filename])
82
91
  end
83
92
 
84
- context.metadata.merge(data: css, sass_dependencies: sass_dependencies)
93
+ context.metadata.merge(data: css, sass_dependencies: sass_dependencies, map: map)
85
94
  end
86
95
 
96
+ private
97
+
87
98
  # Public: Build the cache store to be used by the Sass engine.
88
99
  #
89
100
  # input - the input hash.
@@ -94,7 +105,17 @@ module Sprockets
94
105
  def build_cache_store(input, version)
95
106
  CacheStore.new(input[:cache], version)
96
107
  end
97
- private :build_cache_store
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
118
+ end
98
119
 
99
120
  # Public: Functions injected into Sass context during Sprockets evaluation.
100
121
  #
@@ -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