sprockets 3.0.0 → 4.0.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.
Files changed (95) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +397 -408
  4. data/bin/sprockets +12 -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 +19 -23
  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 +59 -11
  22. data/lib/sprockets/bower.rb +5 -2
  23. data/lib/sprockets/bundle.rb +44 -4
  24. data/lib/sprockets/cache/file_store.rb +32 -7
  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 +42 -5
  28. data/lib/sprockets/cached_environment.rb +14 -19
  29. data/lib/sprockets/closure_compressor.rb +6 -11
  30. data/lib/sprockets/coffee_script_processor.rb +19 -5
  31. data/lib/sprockets/compressing.rb +62 -2
  32. data/lib/sprockets/configuration.rb +3 -7
  33. data/lib/sprockets/context.rb +98 -23
  34. data/lib/sprockets/dependencies.rb +9 -8
  35. data/lib/sprockets/digest_utils.rb +104 -60
  36. data/lib/sprockets/directive_processor.rb +45 -35
  37. data/lib/sprockets/eco_processor.rb +3 -2
  38. data/lib/sprockets/ejs_processor.rb +3 -2
  39. data/lib/sprockets/encoding_utils.rb +8 -4
  40. data/lib/sprockets/environment.rb +9 -4
  41. data/lib/sprockets/erb_processor.rb +28 -21
  42. data/lib/sprockets/errors.rb +1 -1
  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 +26 -6
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +11 -10
  52. data/lib/sprockets/loader.rb +236 -69
  53. data/lib/sprockets/manifest.rb +97 -44
  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 +106 -21
  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 -51
  63. data/lib/sprockets/processor_utils.rb +81 -15
  64. data/lib/sprockets/resolve.rb +182 -95
  65. data/lib/sprockets/sass_cache_store.rb +1 -0
  66. data/lib/sprockets/sass_compressor.rb +21 -17
  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 +45 -17
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +57 -34
  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 +23 -20
  77. data/lib/sprockets/unloaded_asset.rb +139 -0
  78. data/lib/sprockets/uri_tar.rb +99 -0
  79. data/lib/sprockets/uri_utils.rb +15 -14
  80. data/lib/sprockets/utils/gzip.rb +99 -0
  81. data/lib/sprockets/utils.rb +43 -59
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +5 -14
  84. data/lib/sprockets.rb +103 -33
  85. metadata +151 -22
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/coffee_script_template.rb +0 -6
  88. data/lib/sprockets/eco_template.rb +0 -6
  89. data/lib/sprockets/ejs_template.rb +0 -6
  90. data/lib/sprockets/engines.rb +0 -81
  91. data/lib/sprockets/erb_template.rb +0 -6
  92. data/lib/sprockets/legacy.rb +0 -314
  93. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  94. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  95. data/lib/sprockets/sass_template.rb +0 -7
data/bin/sprockets CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ $VERBOSE = nil
2
3
 
3
4
  require 'sprockets'
4
5
  require 'optparse'
@@ -11,7 +12,7 @@ unless ARGV.delete("--noenv")
11
12
  end
12
13
  end
13
14
 
14
- filenames = []
15
+ paths = []
15
16
  environment = Sprockets::Environment.new(Dir.pwd)
16
17
  manifest = nil
17
18
 
@@ -51,6 +52,10 @@ OptionParser.new do |opts|
51
52
  opts.on("--noenv", "Disables .sprocketsrc file") do
52
53
  end
53
54
 
55
+ opts.on("--cache=DIRECTORY", "Enables the FileStore cache using the specified directory") do |directory|
56
+ environment.cache = Sprockets::Cache::FileStore.new(directory)
57
+ end
58
+
54
59
  opts.on_tail("-h", "--help", "Shows this help message") do
55
60
  opts.show_usage
56
61
  end
@@ -63,8 +68,8 @@ OptionParser.new do |opts|
63
68
  opts.show_usage if ARGV.empty?
64
69
 
65
70
  begin
66
- opts.order(ARGV) do |filename|
67
- filenames << File.expand_path(filename)
71
+ opts.order(ARGV) do |path|
72
+ paths << path
68
73
  end
69
74
  rescue OptionParser::ParseError => e
70
75
  opts.warn e.message
@@ -74,14 +79,14 @@ end
74
79
 
75
80
  if environment.paths.empty?
76
81
  warn "No load paths given"
77
- warn "Usage: sprockets -Ijavascripts/ filename"
82
+ warn "Usage: sprockets -Ijavascripts/ path"
78
83
  exit 1
79
84
  end
80
85
 
81
86
  if manifest
82
- manifest.compile(filenames)
83
- elsif filenames.length == 1
84
- puts environment.find_asset(filenames.first).to_s
87
+ manifest.compile(paths)
88
+ elsif paths.length == 1
89
+ puts environment.find_asset(paths.first).to_s
85
90
  else
86
91
  warn "Only one file can be compiled to stdout at a time"
87
92
  exit 1
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rake'
2
3
  require 'rake/tasklib'
3
4
 
@@ -124,7 +125,7 @@ module Rake
124
125
  end
125
126
  end
126
127
 
127
- task :clobber => ["clobber_#{name}"]
128
+ task clobber: ["clobber_#{name}"]
128
129
 
129
130
  desc name == :assets ? "Clean old assets" : "Clean old #{name} assets"
130
131
  task "clean_#{name}" do
@@ -133,7 +134,7 @@ module Rake
133
134
  end
134
135
  end
135
136
 
136
- task :clean => ["clean_#{name}"]
137
+ task clean: ["clean_#{name}"]
137
138
  end
138
139
 
139
140
  private
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/uri_utils'
3
+ require 'sprockets/path_utils'
4
+
5
+ module Sprockets
6
+ # This is a processor designed to add a source map "comment"
7
+ # to the bottom of a css or JS file that is serving a source
8
+ # map. An example of a comment might look like this
9
+ #
10
+ # //# application.js-80af0efcc960fc2ac93eda2f7b12e3db40ab360bf6ea269ceed3bea3678326f9.map
11
+ #
12
+ # As an asset is built it gets source map information added
13
+ # to the `asset.to_hash[:metadata][:map]` key. This contains all the
14
+ # information that is needed to build a source map file.
15
+ #
16
+ # To add this comment we must have an asset we can link to.
17
+ # To do this we ensure that the original aset is loaded, then
18
+ # we use a use a special mime type. For example `application/js-sourcemap+json`
19
+ # for a JS source map.
20
+ #
21
+ # This will trigger a new asset to be loaded and generated by the
22
+ # `SourceMapProcessor` processor.
23
+ #
24
+ # Finally once we have that file, we can generate a link to it
25
+ # with it's full fingerprint. This is done and then
26
+ # added to the original asset as a comment at the bottom.
27
+ #
28
+ class AddSourceMapCommentToAssetProcessor
29
+ def self.call(input)
30
+
31
+ case input[:content_type]
32
+ when "application/javascript"
33
+ comment = "\n//# sourceMappingURL=%s"
34
+ map_type = "application/js-sourcemap+json"
35
+ when "text/css"
36
+ comment = "\n/*# sourceMappingURL=%s */"
37
+ map_type = "application/css-sourcemap+json"
38
+ else
39
+ fail input[:content_type]
40
+ end
41
+
42
+ env = input[:environment]
43
+
44
+ uri, _ = env.resolve!(input[:filename], accept: input[:content_type])
45
+ asset = env.load(uri)
46
+
47
+ uri, _ = env.resolve!(input[:filename], accept: map_type)
48
+ map = env.load(uri)
49
+
50
+ uri, params = URIUtils.parse_asset_uri(input[:uri])
51
+ uri = env.expand_from_root(params[:index_alias]) if params[:index_alias]
52
+ path = PathUtils.relative_path_from(PathUtils.split_subpath(input[:load_path], uri), map.digest_path)
53
+
54
+ asset.metadata.merge(
55
+ data: asset.source + (comment % path) + "\n",
56
+ links: asset.links + [asset.uri, map.uri]
57
+ )
58
+ end
59
+ end
60
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'fileutils'
2
3
  require 'sprockets/digest_utils'
3
4
 
@@ -13,17 +14,14 @@ module Sprockets
13
14
  # attributes - Hash of ivars
14
15
  #
15
16
  # Returns Asset.
16
- def initialize(environment, attributes = {})
17
- @environment = environment
17
+ def initialize(attributes = {})
18
18
  @attributes = attributes
19
19
  @content_type = attributes[:content_type]
20
20
  @filename = attributes[:filename]
21
21
  @id = attributes[:id]
22
- @integrity = attributes[:integrity]
23
22
  @load_path = attributes[:load_path]
24
23
  @logical_path = attributes[:logical_path]
25
24
  @metadata = attributes[:metadata]
26
- @mtime = attributes[:mtime]
27
25
  @name = attributes[:name]
28
26
  @source = attributes[:source]
29
27
  @uri = attributes[:uri]
@@ -69,6 +67,13 @@ module Sprockets
69
67
  logical_path.sub(/\.(\w+)$/) { |ext| "-#{etag}#{ext}" }
70
68
  end
71
69
 
70
+ # Public: Return load path + logical path with digest spliced in.
71
+ #
72
+ # Returns String.
73
+ def full_digest_path
74
+ File.join(@load_path, digest_path)
75
+ end
76
+
72
77
  # Public: Returns String MIME type of asset. Returns nil if type is unknown.
73
78
  attr_reader :content_type
74
79
 
@@ -81,14 +86,6 @@ module Sprockets
81
86
  metadata[:links] || Set.new
82
87
  end
83
88
 
84
- # Public: Get all internally required assets that were concated into this
85
- # asset.
86
- #
87
- # Returns Array of String asset URIs.
88
- def included
89
- metadata[:included]
90
- end
91
-
92
89
  # Public: Return `String` of concatenated source.
93
90
  #
94
91
  # Returns String.
@@ -121,26 +118,28 @@ module Sprockets
121
118
  end
122
119
  alias_method :bytesize, :length
123
120
 
121
+ # Public: Returns String byte digest of source.
122
+ def digest
123
+ metadata[:digest]
124
+ end
125
+
124
126
  # Public: Returns String hexdigest of source.
125
127
  def hexdigest
126
- DigestUtils.pack_hexdigest(metadata[:digest])
128
+ DigestUtils.pack_hexdigest(digest)
127
129
  end
128
130
 
129
- # Deprecated: Returns String hexdigest of source.
130
- #
131
- # In 4.x this will be changed to return a raw Digest byte String.
132
- alias_method :digest, :hexdigest
133
-
134
131
  # Pubic: ETag String of Asset.
135
132
  alias_method :etag, :hexdigest
136
133
 
137
134
  # Public: Returns String base64 digest of source.
138
135
  def base64digest
139
- DigestUtils.pack_base64digest(metadata[:digest])
136
+ DigestUtils.pack_base64digest(digest)
140
137
  end
141
138
 
142
139
  # Public: A "named information" URL for subresource integrity.
143
- attr_reader :integrity
140
+ def integrity
141
+ DigestUtils.integrity_uri(metadata[:digest])
142
+ end
144
143
 
145
144
  # Public: Add enumerator to allow `Asset` instances to be used as Rack
146
145
  # compatible body objects.
@@ -165,9 +164,6 @@ module Sprockets
165
164
  f.write source
166
165
  end
167
166
 
168
- # Set mtime correctly
169
- File.utime(mtime, mtime, filename)
170
-
171
167
  nil
172
168
  end
173
169
 
@@ -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
@@ -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,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,20 +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'
15
+ require 'sprockets/source_map_utils'
16
+ require 'sprockets/uri_tar'
13
17
 
14
18
  module Sprockets
15
- # `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`.
16
32
  class Base
17
- include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils
33
+ include PathUtils, PathDependencyUtils, PathDigestUtils, DigestUtils, SourceMapUtils
18
34
  include Configuration
19
35
  include Server
20
36
  include Resolve, Loader
21
37
  include Bower
38
+ include Npm
22
39
 
23
40
  # Get persistent cache store
24
41
  attr_reader :cache
@@ -32,7 +49,7 @@ module Sprockets
32
49
  @cache = Cache.new(cache, logger)
33
50
  end
34
51
 
35
- # Return an `Cached`. Must be implemented by the subclass.
52
+ # Return an `CachedEnvironment`. Must be implemented by the subclass.
36
53
  def cached
37
54
  raise NotImplementedError
38
55
  end
@@ -48,33 +65,46 @@ module Sprockets
48
65
  # Caveat: Digests are cached by the path's current mtime. Its possible
49
66
  # for a files contents to have changed and its mtime to have been
50
67
  # negligently reset thus appearing as if the file hasn't changed on
51
- # disk. Also, the mtime is only read to the nearest second. Its
68
+ # disk. Also, the mtime is only read to the nearest second. It's
52
69
  # also possible the file was updated more than once in a given second.
53
- cache.fetch("file_digest:#{path}:#{stat.mtime.to_i}") do
70
+ key = UnloadedAsset.new(path, self).file_digest_key(stat.mtime.to_i)
71
+ cache.fetch(key) do
54
72
  self.stat_digest(path, stat)
55
73
  end
56
74
  end
57
75
  end
58
76
 
59
77
  # Find asset by logical path or expanded path.
60
- def find_asset(path, options = {})
61
- uri, _ = resolve(path, options.merge(compat: false))
78
+ def find_asset(*args, **options)
79
+ uri, _ = resolve(*args, **options)
62
80
  if uri
63
81
  load(uri)
64
82
  end
65
83
  end
66
84
 
67
- def find_all_linked_assets(path, options = {})
68
- 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?
69
87
 
70
- asset = find_asset(path, options)
88
+ parent_asset = asset = find_asset(*args)
71
89
  return unless asset
72
90
 
73
91
  yield asset
74
92
  stack = asset.links.to_a
93
+ linked_paths = {}
75
94
 
76
95
  while uri = stack.shift
77
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
78
108
  stack = asset.links.to_a + stack
79
109
  end
80
110
 
@@ -89,11 +119,29 @@ module Sprockets
89
119
  find_asset(*args)
90
120
  end
91
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
130
+ end
131
+
92
132
  # Pretty inspect
93
133
  def inspect
94
134
  "#<#{self.class}:0x#{object_id.to_s(16)} " +
95
135
  "root=#{root.to_s.inspect}, " +
96
136
  "paths=#{paths.inspect}>"
97
137
  end
138
+
139
+ def compress_from_root(uri)
140
+ URITar.new(uri, self).compress
141
+ end
142
+
143
+ def expand_from_root(uri)
144
+ URITar.new(uri, self).expand
145
+ end
98
146
  end
99
147
  end
@@ -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,20 +50,41 @@ 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
- initial[k] = v if !v.nil?
83
+ if v.respond_to?(:call)
84
+ initial[k] = v.call(input)
85
+ elsif !v.nil?
86
+ initial[k] = v
87
+ end
48
88
  end
49
89
 
50
90
  assets.reduce(initial) do |h, asset|