sprockets 3.7.2 → 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 (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
data/bin/sprockets CHANGED
@@ -12,7 +12,7 @@ unless ARGV.delete("--noenv")
12
12
  end
13
13
  end
14
14
 
15
- filenames = []
15
+ paths = []
16
16
  environment = Sprockets::Environment.new(Dir.pwd)
17
17
  manifest = nil
18
18
 
@@ -52,6 +52,10 @@ OptionParser.new do |opts|
52
52
  opts.on("--noenv", "Disables .sprocketsrc file") do
53
53
  end
54
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
+
55
59
  opts.on_tail("-h", "--help", "Shows this help message") do
56
60
  opts.show_usage
57
61
  end
@@ -64,8 +68,8 @@ OptionParser.new do |opts|
64
68
  opts.show_usage if ARGV.empty?
65
69
 
66
70
  begin
67
- opts.order(ARGV) do |filename|
68
- filenames << File.expand_path(filename)
71
+ opts.order(ARGV) do |path|
72
+ paths << path
69
73
  end
70
74
  rescue OptionParser::ParseError => e
71
75
  opts.warn e.message
@@ -75,14 +79,14 @@ end
75
79
 
76
80
  if environment.paths.empty?
77
81
  warn "No load paths given"
78
- warn "Usage: sprockets -Ijavascripts/ filename"
82
+ warn "Usage: sprockets -Ijavascripts/ path"
79
83
  exit 1
80
84
  end
81
85
 
82
86
  if manifest
83
- manifest.compile(filenames)
84
- elsif filenames.length == 1
85
- 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
86
90
  else
87
91
  warn "Only one file can be compiled to stdout at a time"
88
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,8 +14,7 @@ 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]
@@ -22,7 +22,6 @@ module Sprockets
22
22
  @load_path = attributes[:load_path]
23
23
  @logical_path = attributes[:logical_path]
24
24
  @metadata = attributes[:metadata]
25
- @mtime = attributes[:mtime]
26
25
  @name = attributes[:name]
27
26
  @source = attributes[:source]
28
27
  @uri = attributes[:uri]
@@ -68,6 +67,13 @@ module Sprockets
68
67
  logical_path.sub(/\.(\w+)$/) { |ext| "-#{etag}#{ext}" }
69
68
  end
70
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
+
71
77
  # Public: Returns String MIME type of asset. Returns nil if type is unknown.
72
78
  attr_reader :content_type
73
79
 
@@ -80,14 +86,6 @@ module Sprockets
80
86
  metadata[:links] || Set.new
81
87
  end
82
88
 
83
- # Public: Get all internally required assets that were concated into this
84
- # asset.
85
- #
86
- # Returns Array of String asset URIs.
87
- def included
88
- metadata[:included]
89
- end
90
-
91
89
  # Public: Return `String` of concatenated source.
92
90
  #
93
91
  # Returns String.
@@ -120,22 +118,22 @@ module Sprockets
120
118
  end
121
119
  alias_method :bytesize, :length
122
120
 
121
+ # Public: Returns String byte digest of source.
122
+ def digest
123
+ metadata[:digest]
124
+ end
125
+
123
126
  # Public: Returns String hexdigest of source.
124
127
  def hexdigest
125
- DigestUtils.pack_hexdigest(metadata[:digest])
128
+ DigestUtils.pack_hexdigest(digest)
126
129
  end
127
130
 
128
- # Deprecated: Returns String hexdigest of source.
129
- #
130
- # In 4.x this will be changed to return a raw Digest byte String.
131
- alias_method :digest, :hexdigest
132
-
133
131
  # Pubic: ETag String of Asset.
134
132
  alias_method :etag, :hexdigest
135
133
 
136
134
  # Public: Returns String base64 digest of source.
137
135
  def base64digest
138
- DigestUtils.pack_base64digest(metadata[:digest])
136
+ DigestUtils.pack_base64digest(digest)
139
137
  end
140
138
 
141
139
  # Public: A "named information" URL for subresource integrity.
@@ -166,9 +164,6 @@ module Sprockets
166
164
  f.write source
167
165
  end
168
166
 
169
- # Set mtime correctly
170
- File.utime(mtime, mtime, filename)
171
-
172
167
  nil
173
168
  end
174
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,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
 
@@ -92,6 +119,16 @@ module Sprockets
92
119
  find_asset(*args)
93
120
  end
94
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
+
95
132
  # Pretty inspect
96
133
  def inspect
97
134
  "#<#{self.class}:0x#{object_id.to_s(16)} " +
@@ -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 'fileutils'
2
3
  require 'logger'
3
4
  require 'sprockets/encoding_utils'
@@ -19,7 +20,9 @@ module Sprockets
19
20
  class FileStore
20
21
  # Internal: Default key limit for store.
21
22
  DEFAULT_MAX_SIZE = 25 * 1024 * 1024
22
-
23
+ EXCLUDED_DIRS = ['.', '..'].freeze
24
+ GITKEEP_FILES = ['.gitkeep', '.keep'].freeze
25
+
23
26
  # Internal: Default standard error fatal logger.
24
27
  #
25
28
  # Returns a Logger.
@@ -32,8 +35,10 @@ module Sprockets
32
35
  # Public: Initialize the cache store.
33
36
  #
34
37
  # root - A String path to a directory to persist cached values to.
35
- # max_size - A Integer of the maximum number of keys the store will hold.
36
- # (default: 1000).
38
+ # max_size - A Integer of the maximum size the store will hold (in bytes).
39
+ # (default: 25MB).
40
+ # logger - The logger to which some info will be printed.
41
+ # (default logger level is FATAL and won't output anything).
37
42
  def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger)
38
43
  @root = root
39
44
  @max_size = max_size
@@ -122,6 +127,23 @@ module Sprockets
122
127
  "#<#{self.class} size=#{size}/#{@max_size}>"
123
128
  end
124
129
 
130
+ # Public: Clear the cache
131
+ #
132
+ # adapted from ActiveSupport::Cache::FileStore#clear
133
+ #
134
+ # Deletes all items from the cache. In this case it deletes all the entries in the specified
135
+ # file store directory except for .keep or .gitkeep. Be careful which directory is specified
136
+ # as @root because everything in that directory will be deleted.
137
+ #
138
+ # Returns true
139
+ def clear(options=nil)
140
+ if File.exist?(@root)
141
+ root_dirs = Dir.entries(@root).reject { |f| (EXCLUDED_DIRS + GITKEEP_FILES).include?(f) }
142
+ FileUtils.rm_r(root_dirs.collect{ |f| File.join(@root, f) })
143
+ end
144
+ true
145
+ end
146
+
125
147
  private
126
148
  # Internal: Get all cache files along with stats.
127
149
  #
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Sprockets
2
3
  class Cache
3
4
  # Public: Basic in memory LRU cache.
@@ -61,6 +62,14 @@ module Sprockets
61
62
  def inspect
62
63
  "#<#{self.class} size=#{@cache.size}/#{@max_size}>"
63
64
  end
65
+
66
+ # Public: Clear the cache
67
+ #
68
+ # Returns true
69
+ def clear(options=nil)
70
+ @cache.clear
71
+ true
72
+ end
64
73
  end
65
74
  end
66
75
  end