sprockets 3.7.2 → 4.0.2

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 +47 -267
  3. data/README.md +477 -321
  4. data/bin/sprockets +11 -7
  5. data/lib/rake/sprocketstask.rb +3 -2
  6. data/lib/sprockets.rb +99 -39
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +31 -23
  9. data/lib/sprockets/autoload.rb +5 -0
  10. data/lib/sprockets/autoload/babel.rb +8 -0
  11. data/lib/sprockets/autoload/closure.rb +1 -0
  12. data/lib/sprockets/autoload/coffee_script.rb +1 -0
  13. data/lib/sprockets/autoload/eco.rb +1 -0
  14. data/lib/sprockets/autoload/ejs.rb +1 -0
  15. data/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/lib/sprockets/autoload/sass.rb +1 -0
  17. data/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/lib/sprockets/autoload/uglifier.rb +1 -0
  19. data/lib/sprockets/autoload/yui.rb +1 -0
  20. data/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +49 -12
  23. data/lib/sprockets/bower.rb +5 -2
  24. data/lib/sprockets/bundle.rb +40 -4
  25. data/lib/sprockets/cache.rb +36 -1
  26. data/lib/sprockets/cache/file_store.rb +25 -3
  27. data/lib/sprockets/cache/memory_store.rb +9 -0
  28. data/lib/sprockets/cache/null_store.rb +8 -0
  29. data/lib/sprockets/cached_environment.rb +14 -19
  30. data/lib/sprockets/closure_compressor.rb +1 -0
  31. data/lib/sprockets/coffee_script_processor.rb +18 -4
  32. data/lib/sprockets/compressing.rb +43 -3
  33. data/lib/sprockets/configuration.rb +3 -7
  34. data/lib/sprockets/context.rb +97 -24
  35. data/lib/sprockets/dependencies.rb +1 -0
  36. data/lib/sprockets/digest_utils.rb +25 -5
  37. data/lib/sprockets/directive_processor.rb +45 -35
  38. data/lib/sprockets/eco_processor.rb +1 -0
  39. data/lib/sprockets/ejs_processor.rb +1 -0
  40. data/lib/sprockets/encoding_utils.rb +1 -0
  41. data/lib/sprockets/environment.rb +9 -4
  42. data/lib/sprockets/erb_processor.rb +28 -21
  43. data/lib/sprockets/errors.rb +1 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +1 -0
  50. data/lib/sprockets/http_utils.rb +25 -7
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +11 -10
  53. data/lib/sprockets/loader.rb +87 -67
  54. data/lib/sprockets/manifest.rb +64 -62
  55. data/lib/sprockets/manifest_utils.rb +9 -6
  56. data/lib/sprockets/mime.rb +8 -42
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +3 -11
  59. data/lib/sprockets/path_digest_utils.rb +2 -1
  60. data/lib/sprockets/path_utils.rb +87 -7
  61. data/lib/sprockets/paths.rb +1 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +31 -61
  64. data/lib/sprockets/processor_utils.rb +24 -35
  65. data/lib/sprockets/resolve.rb +177 -93
  66. data/lib/sprockets/sass_cache_store.rb +2 -6
  67. data/lib/sprockets/sass_compressor.rb +13 -1
  68. data/lib/sprockets/sass_functions.rb +1 -0
  69. data/lib/sprockets/sass_importer.rb +1 -0
  70. data/lib/sprockets/sass_processor.rb +30 -9
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +26 -23
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +63 -35
  77. data/lib/sprockets/uglifier_compressor.rb +21 -11
  78. data/lib/sprockets/unloaded_asset.rb +13 -11
  79. data/lib/sprockets/uri_tar.rb +1 -0
  80. data/lib/sprockets/uri_utils.rb +11 -8
  81. data/lib/sprockets/utils.rb +41 -74
  82. data/lib/sprockets/utils/gzip.rb +46 -14
  83. data/lib/sprockets/version.rb +2 -1
  84. data/lib/sprockets/yui_compressor.rb +1 -0
  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,11 +1,16 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
3
  module Autoload
4
+ autoload :Babel, 'sprockets/autoload/babel'
3
5
  autoload :Closure, 'sprockets/autoload/closure'
4
6
  autoload :CoffeeScript, 'sprockets/autoload/coffee_script'
5
7
  autoload :Eco, 'sprockets/autoload/eco'
6
8
  autoload :EJS, 'sprockets/autoload/ejs'
9
+ autoload :JSMinC, 'sprockets/autoload/jsminc'
7
10
  autoload :Sass, 'sprockets/autoload/sass'
11
+ autoload :SassC, 'sprockets/autoload/sassc'
8
12
  autoload :Uglifier, 'sprockets/autoload/uglifier'
9
13
  autoload :YUI, 'sprockets/autoload/yui'
14
+ autoload :Zopfli, 'sprockets/autoload/zopfli'
10
15
  end
11
16
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'babel/transpiler'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ Babel = ::Babel
7
+ end
8
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'closure-compiler'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'coffee_script'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'eco'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'ejs'
2
3
 
3
4
  module Sprockets
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'jsminc'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ JSMinC = ::JSMinC
7
+ end
8
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sass'
2
3
 
3
4
  module Sprockets
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'sassc'
3
+
4
+ module Sprockets
5
+ module Autoload
6
+ SassC = ::SassC
7
+ end
8
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'uglifier'
2
3
 
3
4
  module Sprockets
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'yui/compressor'
2
3
 
3
4
  module Sprockets
@@ -0,0 +1,7 @@
1
+ require 'zopfli'
2
+
3
+ module Sprockets
4
+ module Autoload
5
+ Zopfli = ::Zopfli
6
+ end
7
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+ require 'sprockets/path_utils'
4
+ require 'sprockets/source_map_utils'
5
+ require 'json'
6
+
7
+ module Sprockets
8
+ class BabelProcessor
9
+ VERSION = '1'
10
+
11
+ def self.instance
12
+ @instance ||= new
13
+ end
14
+
15
+ def self.call(input)
16
+ instance.call(input)
17
+ end
18
+
19
+ def self.cache_key
20
+ instance.cache_key
21
+ end
22
+
23
+ attr_reader :cache_key
24
+
25
+ def initialize(options = {})
26
+ @options = options.merge({
27
+ 'blacklist' => (options['blacklist'] || []) + ['useStrict'],
28
+ 'sourceMap' => true
29
+ }).freeze
30
+
31
+ @cache_key = [
32
+ self.class.name,
33
+ Autoload::Babel::Transpiler::VERSION,
34
+ Autoload::Babel::Source::VERSION,
35
+ VERSION,
36
+ @options
37
+ ].freeze
38
+ end
39
+
40
+ def call(input)
41
+ data = input[:data]
42
+
43
+ result = input[:cache].fetch(@cache_key + [input[:filename]] + [data]) do
44
+ opts = {
45
+ 'moduleRoot' => nil,
46
+ 'filename' => input[:filename],
47
+ 'filenameRelative' => PathUtils.split_subpath(input[:load_path], input[:filename]),
48
+ 'sourceFileName' => File.basename(input[:filename]),
49
+ 'sourceMapTarget' => input[:filename]
50
+ }.merge(@options)
51
+
52
+ if opts['moduleIds'] && opts['moduleRoot']
53
+ opts['moduleId'] ||= File.join(opts['moduleRoot'], input[:name])
54
+ elsif opts['moduleIds']
55
+ opts['moduleId'] ||= input[:name]
56
+ end
57
+ Autoload::Babel::Transpiler.transform(data, opts)
58
+ end
59
+
60
+ map = SourceMapUtils.format_source_map(result["map"], input)
61
+ map = SourceMapUtils.combine_source_maps(input[:metadata][:map], map)
62
+
63
+ { data: result['code'], map: map }
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'sprockets/asset'
2
3
  require 'sprockets/bower'
3
4
  require 'sprockets/cache'
@@ -5,22 +6,36 @@ require 'sprockets/configuration'
5
6
  require 'sprockets/digest_utils'
6
7
  require 'sprockets/errors'
7
8
  require 'sprockets/loader'
8
- require 'sprockets/path_digest_utils'
9
+ require 'sprockets/npm'
9
10
  require 'sprockets/path_dependency_utils'
11
+ require 'sprockets/path_digest_utils'
10
12
  require 'sprockets/path_utils'
11
13
  require 'sprockets/resolve'
12
14
  require 'sprockets/server'
13
- require 'sprockets/loader'
15
+ require 'sprockets/source_map_utils'
14
16
  require 'sprockets/uri_tar'
15
17
 
16
18
  module Sprockets
17
- # `Base` class for `Environment` and `Cached`.
19
+
20
+ class DoubleLinkError < Sprockets::Error
21
+ def initialize(parent_filename:, logical_path:, last_filename:, filename:)
22
+ super <<~MSG
23
+ Multiple files with the same output path cannot be linked (#{logical_path.inspect})
24
+ In #{parent_filename.inspect} these files were linked:
25
+ - #{last_filename}
26
+ - #{filename}
27
+ MSG
28
+ end
29
+ end
30
+
31
+ # `Base` class for `Environment` and `CachedEnvironment`.
18
32
  class Base
19
- include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils
33
+ include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
20
34
  include Configuration
21
35
  include Server
22
36
  include Resolve, Loader
23
37
  include Bower
38
+ include Npm
24
39
 
25
40
  # Get persistent cache store
26
41
  attr_reader :cache
@@ -34,7 +49,7 @@ module Sprockets
34
49
  @cache = Cache.new(cache, logger)
35
50
  end
36
51
 
37
- # Return an `Cached`. Must be implemented by the subclass.
52
+ # Return an `CachedEnvironment`. Must be implemented by the subclass.
38
53
  def cached
39
54
  raise NotImplementedError
40
55
  end
@@ -60,24 +75,36 @@ module Sprockets
60
75
  end
61
76
 
62
77
  # Find asset by logical path or expanded path.
63
- def find_asset(path, options = {})
64
- uri, _ = resolve(path, options.merge(compat: false))
78
+ def find_asset(*args, **options)
79
+ uri, _ = resolve(*args, **options)
65
80
  if uri
66
81
  load(uri)
67
82
  end
68
83
  end
69
84
 
70
- def find_all_linked_assets(path, options = {})
71
- return to_enum(__method__, path, options) unless block_given?
85
+ def find_all_linked_assets(*args)
86
+ return to_enum(__method__, *args) unless block_given?
72
87
 
73
- asset = find_asset(path, options)
88
+ parent_asset = asset = find_asset(*args)
74
89
  return unless asset
75
90
 
76
91
  yield asset
77
92
  stack = asset.links.to_a
93
+ linked_paths = {}
78
94
 
79
95
  while uri = stack.shift
80
96
  yield asset = load(uri)
97
+
98
+ last_filename = linked_paths[asset.logical_path]
99
+ if last_filename && last_filename != asset.filename
100
+ raise DoubleLinkError.new(
101
+ parent_filename: parent_asset.filename,
102
+ last_filename: last_filename,
103
+ logical_path: asset.logical_path,
104
+ filename: asset.filename
105
+ )
106
+ end
107
+ linked_paths[asset.logical_path] = asset.filename
81
108
  stack = asset.links.to_a + stack
82
109
  end
83
110
 
@@ -88,8 +115,18 @@ module Sprockets
88
115
  #
89
116
  # environment['application.js']
90
117
  #
91
- def [](*args)
92
- find_asset(*args)
118
+ def [](*args, **options)
119
+ find_asset(*args, **options)
120
+ end
121
+
122
+ # Find asset by logical path or expanded path.
123
+ #
124
+ # If the asset is not found an error will be raised.
125
+ def find_asset!(*args)
126
+ uri, _ = resolve!(*args)
127
+ if uri
128
+ load(uri)
129
+ end
93
130
  end
94
131
 
95
132
  # Pretty inspect
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'json'
2
3
 
3
4
  module Sprockets
@@ -17,7 +18,7 @@ module Sprockets
17
18
  candidates, deps = super
18
19
 
19
20
  # bower.json can only be nested one level deep
20
- if !logical_path.index('/')
21
+ if !logical_path.index('/'.freeze)
21
22
  dirname = File.join(load_path, logical_path)
22
23
 
23
24
  if directory?(dirname)
@@ -27,7 +28,9 @@ module Sprockets
27
28
  if filename
28
29
  deps << build_file_digest_uri(filename)
29
30
  read_bower_main(dirname, filename) do |path|
30
- candidates << path
31
+ if file?(path)
32
+ candidates << path
33
+ end
31
34
  end
32
35
  end
33
36
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  require 'set'
2
3
  require 'sprockets/utils'
4
+ require 'sprockets/uri_utils'
3
5
 
4
6
  module Sprockets
5
7
  # Internal: Bundle processor takes a single file asset and prepends all the
@@ -15,15 +17,32 @@ module Sprockets
15
17
  def self.call(input)
16
18
  env = input[:environment]
17
19
  type = input[:content_type]
20
+ input[:links] ||= Set.new
18
21
  dependencies = Set.new(input[:metadata][:dependencies])
19
22
 
20
- processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self, compat: false)
23
+ processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self)
21
24
  dependencies.merge(deps)
22
25
 
26
+ # DirectiveProcessor (and any other transformers called here with pipeline=self)
27
+ primary_asset = env.load(processed_uri)
28
+ to_load = primary_asset.metadata.delete(:to_load) || Set.new
29
+ to_link = primary_asset.metadata.delete(:to_link) || Set.new
30
+
31
+ to_load.each do |uri|
32
+ loaded_asset = env.load(uri)
33
+ dependencies.merge(loaded_asset.metadata[:dependencies])
34
+ if to_link.include?(uri)
35
+ primary_metadata = primary_asset.metadata
36
+ input[:links] << loaded_asset.uri
37
+ primary_metadata[:links] << loaded_asset.uri
38
+ end
39
+ end
40
+
23
41
  find_required = proc { |uri| env.load(uri).metadata[:required] }
24
42
  required = Utils.dfs(processed_uri, &find_required)
25
43
  stubbed = Utils.dfs(env.load(processed_uri).metadata[:stubbed], &find_required)
26
44
  required.subtract(stubbed)
45
+ dedup(required)
27
46
  assets = required.map { |uri| env.load(uri) }
28
47
 
29
48
  (required + stubbed).each do |uri|
@@ -31,21 +50,38 @@ module Sprockets
31
50
  end
32
51
 
33
52
  reducers = Hash[env.match_mime_type_keys(env.config[:bundle_reducers], type).flat_map(&:to_a)]
34
- process_bundle_reducers(assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri))
53
+ process_bundle_reducers(input, assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri))
54
+ end
55
+
56
+ # Internal: Removes uri from required if it's already included as an alias.
57
+ #
58
+ # required - Set of required uris
59
+ #
60
+ # Returns deduped set of uris
61
+ def self.dedup(required)
62
+ dupes = required.reduce([]) do |r, uri|
63
+ path, params = URIUtils.parse_asset_uri(uri)
64
+ if (params.delete(:index_alias))
65
+ r << URIUtils.build_asset_uri(path, params)
66
+ end
67
+ r
68
+ end
69
+ required.subtract(dupes)
35
70
  end
36
71
 
37
72
  # Internal: Run bundle reducers on set of Assets producing a reduced
38
73
  # metadata Hash.
39
74
  #
75
+ # filename - String bundle filename
40
76
  # assets - Array of Assets
41
77
  # reducers - Array of [initial, reducer_proc] pairs
42
78
  #
43
79
  # Returns reduced asset metadata Hash.
44
- def self.process_bundle_reducers(assets, reducers)
80
+ def self.process_bundle_reducers(input, assets, reducers)
45
81
  initial = {}
46
82
  reducers.each do |k, (v, _)|
47
83
  if v.respond_to?(:call)
48
- initial[k] = v.call
84
+ initial[k] = v.call(input)
49
85
  elsif !v.nil?
50
86
  initial[k] = v
51
87
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'logger'
2
3
  require 'sprockets/digest_utils'
3
4
 
@@ -35,6 +36,12 @@ module Sprockets
35
36
  #
36
37
  # Returns argument value.
37
38
  #
39
+ # clear(options)
40
+ #
41
+ # Clear the entire cache. Be careful with this method since it could
42
+ # affect other processes if shared cache is being used.
43
+ #
44
+ # The options hash is passed to the underlying cache implementation.
38
45
  class Cache
39
46
  # Builtin cache stores.
40
47
  autoload :FileStore, 'sprockets/cache/file_store'
@@ -44,7 +51,7 @@ module Sprockets
44
51
  # Internal: Cache key version for this class. Rarely should have to change
45
52
  # unless the cache format radically changes. Will be bump on major version
46
53
  # releases though.
47
- VERSION = '3.0'
54
+ VERSION = '4.0.0'
48
55
 
49
56
  def self.default_logger
50
57
  logger = Logger.new($stderr)
@@ -143,6 +150,14 @@ module Sprockets
143
150
  "#<#{self.class} local=#{@fetch_cache.inspect} store=#{@cache_wrapper.cache.inspect}>"
144
151
  end
145
152
 
153
+ # Public: Clear cache
154
+ #
155
+ # Returns truthy on success, potentially raises exception on failure
156
+ def clear(options=nil)
157
+ @cache_wrapper.clear
158
+ @fetch_cache.clear
159
+ end
160
+
146
161
  private
147
162
  # Internal: Expand object cache key into a short String key.
148
163
  #
@@ -211,6 +226,16 @@ module Sprockets
211
226
  def set(key, value)
212
227
  cache.set(key, value)
213
228
  end
229
+
230
+ def clear(options=nil)
231
+ # dalli has a #flush method so try it
232
+ if cache.respond_to?(:flush)
233
+ cache.flush(options)
234
+ else
235
+ cache.clear(options)
236
+ end
237
+ true
238
+ end
214
239
  end
215
240
 
216
241
  class HashWrapper < Wrapper
@@ -221,6 +246,11 @@ module Sprockets
221
246
  def set(key, value)
222
247
  cache[key] = value
223
248
  end
249
+
250
+ def clear(options=nil)
251
+ cache.clear
252
+ true
253
+ end
224
254
  end
225
255
 
226
256
  class ReadWriteWrapper < Wrapper
@@ -231,6 +261,11 @@ module Sprockets
231
261
  def set(key, value)
232
262
  cache.write(key, value)
233
263
  end
264
+
265
+ def clear(options=nil)
266
+ cache.clear(options)
267
+ true
268
+ end
234
269
  end
235
270
  end
236
271
  end