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
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