sprockets 3.7.2 → 4.0.0.beta1

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 (65) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +2 -295
  3. data/README.md +21 -35
  4. data/bin/sprockets +11 -8
  5. data/lib/rake/sprocketstask.rb +2 -2
  6. data/lib/sprockets.rb +79 -34
  7. data/lib/sprockets/asset.rb +8 -21
  8. data/lib/sprockets/autoload.rb +3 -0
  9. data/lib/sprockets/autoload/babel.rb +7 -0
  10. data/lib/sprockets/autoload/jsminc.rb +7 -0
  11. data/lib/sprockets/autoload/sassc.rb +7 -0
  12. data/lib/sprockets/babel_processor.rb +58 -0
  13. data/lib/sprockets/base.rb +8 -8
  14. data/lib/sprockets/bower.rb +4 -2
  15. data/lib/sprockets/bundle.rb +1 -1
  16. data/lib/sprockets/cache.rb +2 -4
  17. data/lib/sprockets/closure_compressor.rb +1 -2
  18. data/lib/sprockets/coffee_script_processor.rb +9 -3
  19. data/lib/sprockets/compressing.rb +2 -2
  20. data/lib/sprockets/configuration.rb +1 -7
  21. data/lib/sprockets/context.rb +10 -18
  22. data/lib/sprockets/digest_utils.rb +40 -52
  23. data/lib/sprockets/directive_processor.rb +10 -13
  24. data/lib/sprockets/http_utils.rb +19 -4
  25. data/lib/sprockets/jsminc_compressor.rb +31 -0
  26. data/lib/sprockets/jst_processor.rb +10 -10
  27. data/lib/sprockets/loader.rb +34 -28
  28. data/lib/sprockets/manifest.rb +3 -35
  29. data/lib/sprockets/manifest_utils.rb +0 -2
  30. data/lib/sprockets/mime.rb +7 -42
  31. data/lib/sprockets/path_dependency_utils.rb +2 -11
  32. data/lib/sprockets/path_digest_utils.rb +1 -1
  33. data/lib/sprockets/path_utils.rb +43 -18
  34. data/lib/sprockets/preprocessors/default_source_map.rb +24 -0
  35. data/lib/sprockets/processing.rb +30 -61
  36. data/lib/sprockets/processor_utils.rb +27 -28
  37. data/lib/sprockets/resolve.rb +172 -92
  38. data/lib/sprockets/sass_cache_store.rb +1 -6
  39. data/lib/sprockets/sass_compressor.rb +14 -1
  40. data/lib/sprockets/sass_processor.rb +18 -8
  41. data/lib/sprockets/sassc_compressor.rb +30 -0
  42. data/lib/sprockets/sassc_processor.rb +68 -0
  43. data/lib/sprockets/server.rb +9 -20
  44. data/lib/sprockets/source_map_comment_processor.rb +29 -0
  45. data/lib/sprockets/source_map_processor.rb +40 -0
  46. data/lib/sprockets/source_map_utils.rb +345 -0
  47. data/lib/sprockets/transformers.rb +62 -35
  48. data/lib/sprockets/uglifier_compressor.rb +12 -5
  49. data/lib/sprockets/unloaded_asset.rb +12 -11
  50. data/lib/sprockets/uri_tar.rb +4 -2
  51. data/lib/sprockets/uri_utils.rb +5 -5
  52. data/lib/sprockets/utils.rb +30 -78
  53. data/lib/sprockets/version.rb +1 -1
  54. metadata +62 -20
  55. data/LICENSE +0 -21
  56. data/lib/sprockets/coffee_script_template.rb +0 -17
  57. data/lib/sprockets/deprecation.rb +0 -90
  58. data/lib/sprockets/eco_template.rb +0 -17
  59. data/lib/sprockets/ejs_template.rb +0 -17
  60. data/lib/sprockets/engines.rb +0 -92
  61. data/lib/sprockets/erb_template.rb +0 -11
  62. data/lib/sprockets/legacy.rb +0 -330
  63. data/lib/sprockets/legacy_proc_processor.rb +0 -35
  64. data/lib/sprockets/legacy_tilt_processor.rb +0 -29
  65. data/lib/sprockets/sass_template.rb +0 -19
@@ -0,0 +1,24 @@
1
+ module Sprockets
2
+ module Preprocessors
3
+ # Private: Adds a default map to assets when one is not present
4
+ #
5
+ # If the input file already has a source map, it effectively returns the original
6
+ # result. Otherwise it maps 1 for 1 lines original to generated. This is needed
7
+ # Because other generators run after might depend on having a valid source map
8
+ # available.
9
+ class DefaultSourceMap
10
+ def call(input)
11
+ result = { data: input[:data] }
12
+ map = input[:metadata][:map]
13
+ if map.nil? || map.empty?
14
+ result[:map] ||= []
15
+ input[:data].each_line.with_index do |_, index|
16
+ line = index + 1
17
+ result[:map] << { source: input[:source_path], generated: [line , 0], original: [line, 0] }
18
+ end
19
+ end
20
+ return result
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,7 +1,4 @@
1
- require 'sprockets/engines'
2
1
  require 'sprockets/file_reader'
3
- require 'sprockets/legacy_proc_processor'
4
- require 'sprockets/legacy_tilt_processor'
5
2
  require 'sprockets/mime'
6
3
  require 'sprockets/processor_utils'
7
4
  require 'sprockets/uri_utils'
@@ -17,9 +14,14 @@ module Sprockets
17
14
  config[:pipelines]
18
15
  end
19
16
 
17
+ # Registers a pipeline that will be called by `call_processor` method.
20
18
  def register_pipeline(name, proc = nil, &block)
21
19
  proc ||= block
22
20
 
21
+ self.config = hash_reassoc(config, :pipeline_exts) do |pipeline_exts|
22
+ pipeline_exts.merge(".#{name}".freeze => name.to_sym)
23
+ end
24
+
23
25
  self.config = hash_reassoc(config, :pipelines) do |pipelines|
24
26
  pipelines.merge(name.to_sym => proc)
25
27
  end
@@ -43,12 +45,13 @@ module Sprockets
43
45
  #
44
46
  # A block can be passed for to create a shorthand processor.
45
47
  #
46
- # register_preprocessor 'text/css', :my_processor do |context, data|
47
- # data.gsub(...)
48
+ # register_preprocessor 'text/css' do |input|
49
+ # input[:data].gsub(...)
48
50
  # end
49
51
  #
50
52
  def register_preprocessor(*args, &block)
51
53
  register_config_processor(:preprocessors, *args, &block)
54
+ compute_transformers!(self.config[:registered_transformers])
52
55
  end
53
56
  alias_method :register_processor, :register_preprocessor
54
57
 
@@ -58,12 +61,13 @@ module Sprockets
58
61
  #
59
62
  # A block can be passed for to create a shorthand processor.
60
63
  #
61
- # register_postprocessor 'application/javascript', :my_processor do |context, data|
62
- # data.gsub(...)
64
+ # register_postprocessor 'application/javascript' do |input|
65
+ # input[:data].gsub(...)
63
66
  # end
64
67
  #
65
68
  def register_postprocessor(*args, &block)
66
69
  register_config_processor(:postprocessors, *args, &block)
70
+ compute_transformers!(self.config[:registered_transformers])
67
71
  end
68
72
 
69
73
  # Remove Preprocessor `klass` for `mime_type`.
@@ -72,6 +76,7 @@ module Sprockets
72
76
  #
73
77
  def unregister_preprocessor(*args)
74
78
  unregister_config_processor(:preprocessors, *args)
79
+ compute_transformers!(self.config[:registered_transformers])
75
80
  end
76
81
  alias_method :unregister_processor, :unregister_preprocessor
77
82
 
@@ -81,6 +86,7 @@ module Sprockets
81
86
  #
82
87
  def unregister_postprocessor(*args)
83
88
  unregister_config_processor(:postprocessors, *args)
89
+ compute_transformers!(self.config[:registered_transformers])
84
90
  end
85
91
 
86
92
  # Bundle Processors are ran on concatenated assets rather than
@@ -95,8 +101,8 @@ module Sprockets
95
101
  #
96
102
  # A block can be passed for to create a shorthand processor.
97
103
  #
98
- # register_bundle_processor 'application/javascript', :my_processor do |context, data|
99
- # data.gsub(...)
104
+ # register_bundle_processor 'application/javascript' do |input|
105
+ # input[:data].gsub(...)
100
106
  # end
101
107
  #
102
108
  def register_bundle_processor(*args, &block)
@@ -154,46 +160,44 @@ module Sprockets
154
160
  protected
155
161
  def resolve_processors_cache_key_uri(uri)
156
162
  params = parse_uri_query_params(uri[11..-1])
157
- params[:engine_extnames] = params[:engines] ? params[:engines].split(',') : []
158
- processors = processors_for(params[:type], params[:file_type], params[:engine_extnames], params[:pipeline])
163
+ processors = processors_for(params[:type], params[:file_type], params[:pipeline])
159
164
  processors_cache_keys(processors)
160
165
  end
161
166
 
162
- def build_processors_uri(type, file_type, engine_extnames, pipeline)
163
- engines = engine_extnames.join(',') if engine_extnames.any?
167
+ def build_processors_uri(type, file_type, pipeline)
164
168
  query = encode_uri_query_params(
165
169
  type: type,
166
170
  file_type: file_type,
167
- engines: engines,
168
171
  pipeline: pipeline
169
172
  )
170
173
  "processors:#{query}"
171
174
  end
172
175
 
173
- def processors_for(type, file_type, engine_extnames, pipeline)
176
+ def processors_for(type, file_type, pipeline)
174
177
  pipeline ||= :default
175
- config[:pipelines][pipeline.to_sym].call(self, type, file_type, engine_extnames)
178
+ if fn = config[:pipelines][pipeline.to_sym]
179
+ fn.call(self, type, file_type)
180
+ else
181
+ raise Error, "no pipeline: #{pipeline}"
182
+ end
176
183
  end
177
184
 
178
- def default_processors_for(type, file_type, engine_extnames)
185
+ def default_processors_for(type, file_type)
179
186
  bundled_processors = config[:bundle_processors][type]
180
187
  if bundled_processors.any?
181
188
  bundled_processors
182
189
  else
183
- self_processors_for(type, file_type, engine_extnames)
190
+ self_processors_for(type, file_type)
184
191
  end
185
192
  end
186
193
 
187
- def self_processors_for(type, file_type, engine_extnames)
194
+ def self_processors_for(type, file_type)
188
195
  processors = []
189
196
 
190
197
  processors.concat config[:postprocessors][type]
191
-
192
198
  if type != file_type && processor = config[:transformers][file_type][type]
193
199
  processors << processor
194
200
  end
195
-
196
- processors.concat engine_extnames.map { |ext| engines[ext] }
197
201
  processors.concat config[:preprocessors][file_type]
198
202
 
199
203
  if processors.any? || mime_type_charset_detecter(type)
@@ -204,55 +208,20 @@ module Sprockets
204
208
  end
205
209
 
206
210
  private
207
- def register_config_processor(type, mime_type, klass, proc = nil, &block)
208
- proc ||= block
209
- processor = wrap_processor(klass, proc)
211
+ def register_config_processor(type, mime_type, processor = nil, &block)
212
+ processor ||= block
210
213
 
211
214
  self.config = hash_reassoc(config, type, mime_type) do |processors|
212
215
  processors.unshift(processor)
213
216
  processors
214
217
  end
215
-
216
- compute_transformers!
217
218
  end
218
219
 
219
- def unregister_config_processor(type, mime_type, klass)
220
- if klass.is_a?(String) || klass.is_a?(Symbol)
221
- klass = config[type][mime_type].detect do |cls|
222
- cls.respond_to?(:name) && cls.name == "Sprockets::LegacyProcProcessor (#{klass})"
223
- end
224
- end
225
-
220
+ def unregister_config_processor(type, mime_type, proccessor)
226
221
  self.config = hash_reassoc(config, type, mime_type) do |processors|
227
- processors.delete(klass)
222
+ processors.delete(proccessor)
228
223
  processors
229
224
  end
230
-
231
- compute_transformers!
232
- end
233
-
234
- def deprecate_legacy_processor_interface(interface)
235
- msg = "You are using a deprecated processor interface #{ interface.inspect }.\n" +
236
- "Please update your processor interface:\n" +
237
- "https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md#supporting-all-versions-of-sprockets-in-processors\n"
238
-
239
- Deprecation.new([caller[3]]).warn msg
240
- end
241
-
242
- def wrap_processor(klass, proc)
243
- if !proc
244
- if klass.respond_to?(:call)
245
- klass
246
- else
247
- deprecate_legacy_processor_interface(klass)
248
- LegacyTiltProcessor.new(klass)
249
- end
250
- elsif proc.respond_to?(:arity) && proc.arity == 2
251
- deprecate_legacy_processor_interface(proc)
252
- LegacyProcProcessor.new(klass.to_s, proc)
253
- else
254
- proc
255
- end
256
225
  end
257
226
  end
258
227
  end
@@ -16,26 +16,34 @@ module Sprockets
16
16
  module ProcessorUtils
17
17
  extend self
18
18
 
19
+ class CompositeProcessor < Struct.new(:processor_strategy, :param, :processors) # :nodoc:
20
+ SINGULAR = lambda { |param, input| ProcessorUtils.call_processor param, input }
21
+ PLURAL = lambda { |param, input| ProcessorUtils.call_processors param, input }
22
+
23
+ def self.create(processors)
24
+ if processors.length == 1
25
+ new SINGULAR, processors.first, processors
26
+ else
27
+ new PLURAL, processors, processors
28
+ end
29
+ end
30
+
31
+ def call(input)
32
+ processor_strategy.call param, input
33
+ end
34
+
35
+ def cache_key
36
+ ProcessorUtils.processors_cache_keys(processors)
37
+ end
38
+ end
39
+
19
40
  # Public: Compose processors in right to left order.
20
41
  #
21
42
  # processors - Array of processors callables
22
43
  #
23
44
  # Returns a composed Proc.
24
45
  def compose_processors(*processors)
25
- context = self
26
-
27
- if processors.length == 1
28
- obj = method(:call_processor).to_proc.curry[processors.first]
29
- else
30
- obj = method(:call_processors).to_proc.curry[processors]
31
- end
32
-
33
- metaclass = (class << obj; self; end)
34
- metaclass.send(:define_method, :cache_key) do
35
- context.processors_cache_keys(processors)
36
- end
37
-
38
- obj
46
+ CompositeProcessor.create processors
39
47
  end
40
48
 
41
49
  # Public: Invoke list of processors in right to left order.
@@ -107,10 +115,12 @@ module Sprockets
107
115
  VALID_METADATA_VALUE_TYPES = Set.new([
108
116
  String,
109
117
  Symbol,
118
+ Fixnum,
119
+ Bignum,
110
120
  TrueClass,
111
121
  FalseClass,
112
122
  NilClass
113
- ] + (0.class == Integer ? [Integer] : [Bignum, Fixnum])).freeze
123
+ ]).freeze
114
124
 
115
125
  # Internal: Set of all nested compound metadata types that can nest values.
116
126
  VALID_METADATA_COMPOUND_TYPES = Set.new([
@@ -119,17 +129,6 @@ module Sprockets
119
129
  Set
120
130
  ]).freeze
121
131
 
122
- # Internal: Hash of all "simple" value types allowed to be returned in
123
- # processor metadata.
124
- VALID_METADATA_VALUE_TYPES_HASH = VALID_METADATA_VALUE_TYPES.each_with_object({}) do |type, hash|
125
- hash[type] = true
126
- end.freeze
127
-
128
- # Internal: Hash of all nested compound metadata types that can nest values.
129
- VALID_METADATA_COMPOUND_TYPES_HASH = VALID_METADATA_COMPOUND_TYPES.each_with_object({}) do |type, hash|
130
- hash[type] = true
131
- end.freeze
132
-
133
132
  # Internal: Set of all allowed metadata types.
134
133
  VALID_METADATA_TYPES = (VALID_METADATA_VALUE_TYPES + VALID_METADATA_COMPOUND_TYPES).freeze
135
134
 
@@ -168,9 +167,9 @@ module Sprockets
168
167
  #
169
168
  # Returns true if class is in whitelist otherwise false.
170
169
  def valid_processor_metadata_value?(value)
171
- if VALID_METADATA_VALUE_TYPES_HASH[value.class]
170
+ if VALID_METADATA_VALUE_TYPES.include?(value.class)
172
171
  true
173
- elsif VALID_METADATA_COMPOUND_TYPES_HASH[value.class]
172
+ elsif VALID_METADATA_COMPOUND_TYPES.include?(value.class)
174
173
  value.all? { |v| valid_processor_metadata_value?(v) }
175
174
  else
176
175
  false
@@ -20,27 +20,21 @@ module Sprockets
20
20
  # # => "file:///path/to/app/javascripts/application.coffee?type=application/javascript"
21
21
  #
22
22
  # The String Asset URI is returned or nil if no results are found.
23
- def resolve(path, options = {})
24
- path = path.to_s
25
- paths = options[:load_paths] || config[:paths]
26
- accept = options[:accept]
23
+ def resolve(path, load_paths: config[:paths], accept: nil, pipeline: nil, base_path: nil)
24
+ paths = load_paths
27
25
 
28
26
  if valid_asset_uri?(path)
29
27
  uri, deps = resolve_asset_uri(path)
30
28
  elsif absolute_path?(path)
31
29
  filename, type, deps = resolve_absolute_path(paths, path, accept)
32
30
  elsif relative_path?(path)
33
- filename, type, pipeline, deps = resolve_relative_path(paths, path, options[:base_path], accept)
31
+ filename, type, path_pipeline, deps, index_alias = resolve_relative_path(paths, path, base_path, accept)
34
32
  else
35
- filename, type, pipeline, deps = resolve_logical_path(paths, path, accept)
33
+ filename, type, path_pipeline, deps, index_alias = resolve_logical_path(paths, path, accept)
36
34
  end
37
35
 
38
36
  if filename
39
- params = {}
40
- params[:type] = type if type
41
- params[:pipeline] = pipeline if pipeline
42
- params[:pipeline] = options[:pipeline] if options[:pipeline]
43
- uri = build_asset_uri(filename, params)
37
+ uri = build_asset_uri(filename, type: type, pipeline: pipeline || path_pipeline, index_alias: index_alias)
44
38
  end
45
39
 
46
40
  return uri, deps
@@ -48,19 +42,18 @@ module Sprockets
48
42
 
49
43
  # Public: Same as resolve() but raises a FileNotFound exception instead of
50
44
  # nil if no assets are found.
51
- def resolve!(path, options = {})
52
- uri, deps = resolve(path, options.merge(compat: false))
45
+ def resolve!(path, **kargs)
46
+ uri, deps = resolve(path, **kargs)
53
47
 
54
48
  unless uri
55
49
  message = "couldn't find file '#{path}'"
56
50
 
57
- if relative_path?(path) && options[:base_path]
58
- load_path, _ = paths_split(config[:paths], options[:base_path])
51
+ if relative_path?(path) && kargs[:base_path]
52
+ load_path, _ = paths_split(config[:paths], kargs[:base_path])
59
53
  message << " under '#{load_path}'"
60
54
  end
61
55
 
62
- message << " with type '#{options[:accept]}'" if options[:accept]
63
- message << "\nChecked in these paths: \n #{ config[:paths].join("\n ") }"
56
+ message << " with type '#{kargs[:accept]}'" if kargs[:accept]
64
57
 
65
58
  raise FileNotFound, message
66
59
  end
@@ -69,143 +62,230 @@ module Sprockets
69
62
  end
70
63
 
71
64
  protected
65
+
66
+ # Internal: Finds an asset given a URI
67
+ #
68
+ # uri - String. Contains file:// scheme, absolute path to
69
+ # file.
70
+ # e.g. "file:///Users/schneems/sprockets/test/fixtures/default/gallery.js?type=application/javascript"
71
+ #
72
+ # Returns Array. Contains a String uri and Set of dependencies
72
73
  def resolve_asset_uri(uri)
73
- filename, _ = parse_asset_uri(uri)
74
- return uri, Set.new([build_file_digest_uri(filename)])
74
+ filename, _ = URIUtils.parse_asset_uri(uri)
75
+ return uri, Set.new( [URIUtils.build_file_digest_uri(filename)] )
75
76
  end
76
77
 
78
+ # Internal: Finds a file in a set of given paths
79
+ #
80
+ # paths - Array of Strings.
81
+ # filename - String containing absolute path to a file including extension.
82
+ # e.g. "/Users/schneems/sprockets/test/fixtures/asset/application.js"
83
+ # accept - String. A Quality value incoded set of
84
+ # mime types that we are looking for. Can be nil.
85
+ # e.g. "application/javascript" or "text/css, */*"
86
+ #
87
+ # Returns Array. Filename, type, path_pipeline, deps, index_alias
77
88
  def resolve_absolute_path(paths, filename, accept)
78
89
  deps = Set.new
79
90
  filename = File.expand_path(filename)
80
91
 
81
92
  # Ensure path is under load paths
82
- return nil, nil, deps unless paths_split(paths, filename)
93
+ return nil, nil, deps unless PathUtils.paths_split(paths, filename)
83
94
 
84
- _, mime_type, _, _ = parse_path_extnames(filename)
95
+ _, mime_type = PathUtils.match_path_extname(filename, config[:mime_exts])
85
96
  type = resolve_transform_type(mime_type, accept)
86
97
  return nil, nil, deps if accept && !type
87
98
 
88
99
  return nil, nil, deps unless file?(filename)
89
100
 
90
- deps << build_file_digest_uri(filename)
101
+ deps << URIUtils.build_file_digest_uri(filename)
91
102
  return filename, type, deps
92
103
  end
93
104
 
105
+ # Internal: Finds a relative file in a set of given paths
106
+ #
107
+ # paths - Array of Strings.
108
+ # path - String. A relative filename with or without extension
109
+ # e.g. "./jquery" or "../foo.js"
110
+ # dirname - String. Base path where we start looking for the given file.
111
+ # accept - String. A Quality value incoded set of
112
+ # mime types that we are looking for. Can be nil.
113
+ # e.g. "application/javascript" or "text/css, */*"
114
+ #
115
+ # Returns Array. Filename, type, path_pipeline, deps, index_alias
94
116
  def resolve_relative_path(paths, path, dirname, accept)
95
117
  filename = File.expand_path(path, dirname)
96
- load_path, _ = paths_split(paths, dirname)
97
- if load_path && logical_path = split_subpath(load_path, filename)
118
+ load_path, _ = PathUtils.paths_split(paths, dirname)
119
+ if load_path && logical_path = PathUtils.split_subpath(load_path, filename)
98
120
  resolve_logical_path([load_path], logical_path, accept)
99
121
  else
100
- return nil, nil, Set.new
122
+ return nil, nil, nil, Set.new
101
123
  end
102
124
  end
103
125
 
126
+ # Internal: Finds a file in a set of given paths
127
+ #
128
+ # paths - Array of Strings.
129
+ # logical_path - String. A filename with extension
130
+ # e.g. "coffee/foo.js" or "foo.js"
131
+ # accept - String. A Quality value incoded set of
132
+ # mime types that we are looking for. Can be nil.
133
+ # e.g. "application/javascript" or "text/css, */*"
134
+ #
135
+ # Finds a file on the given paths.
136
+ #
137
+ # Returns Array. Filename, type, path_pipeline, deps, index_alias
104
138
  def resolve_logical_path(paths, logical_path, accept)
105
- logical_name, mime_type, _, pipeline = parse_path_extnames(logical_path)
139
+ extname, mime_type = PathUtils.match_path_extname(logical_path, config[:mime_exts])
140
+ logical_name = logical_path.chomp(extname)
141
+
142
+ extname, pipeline = PathUtils.match_path_extname(logical_name, config[:pipeline_exts])
143
+ logical_name = logical_name.chomp(extname)
144
+
106
145
  parsed_accept = parse_accept_options(mime_type, accept)
107
146
  transformed_accepts = expand_transform_accepts(parsed_accept)
108
- filename, mime_type, deps = resolve_under_paths(paths, logical_name, transformed_accepts)
147
+
148
+ filename, mime_type, deps, index_alias = resolve_under_paths(paths, logical_name, transformed_accepts)
109
149
 
110
150
  if filename
111
151
  deps << build_file_digest_uri(filename)
112
152
  type = resolve_transform_type(mime_type, parsed_accept)
113
- return filename, type, pipeline, deps
153
+ return filename, type, pipeline, deps, index_alias
114
154
  else
115
155
  return nil, nil, nil, deps
116
156
  end
117
157
  end
118
158
 
159
+ # Internal: Finds a file in a set of given paths
160
+ #
161
+ # paths - Array of Strings.
162
+ # logical_name - String. A filename without extension
163
+ # e.g. "application" or "coffee/foo"
164
+ # accepts - Array of array containing mime/version pairs
165
+ # e.g. [["application/javascript", 1.0]]
166
+ #
167
+ # Finds a file with the same name as `logical_name` or "index" inside
168
+ # of the `logical_name` directory that matches a valid mime-type/version from
169
+ # `accepts`.
170
+ #
171
+ # Returns Array. Filename, type, dependencies, and index_alias
119
172
  def resolve_under_paths(paths, logical_name, accepts)
120
- all_deps = Set.new
121
- return nil, nil, all_deps if accepts.empty?
173
+ deps = Set.new
174
+ return nil, nil, deps if accepts.empty?
175
+
176
+ # TODO: Allow new path resolves to be registered
177
+ @resolvers ||= [
178
+ method(:resolve_main_under_path),
179
+ method(:resolve_alts_under_path),
180
+ method(:resolve_index_under_path)
181
+ ]
182
+ mime_exts = config[:mime_exts]
122
183
 
123
- logical_basename = File.basename(logical_name)
124
184
  paths.each do |load_path|
125
- candidates, deps = path_matches(load_path, logical_name, logical_basename)
126
- all_deps.merge(deps)
127
- candidate = find_best_q_match(accepts, candidates) do |c, matcher|
128
- match_mime_type?(c[1] || "application/octet-stream", matcher)
185
+ candidates = []
186
+ @resolvers.each do |fn|
187
+ result = fn.call(load_path, logical_name, mime_exts)
188
+ candidates.concat(result[0])
189
+ deps.merge(result[1])
129
190
  end
130
- return candidate + [all_deps] if candidate
131
- end
132
191
 
133
- return nil, nil, all_deps
134
- end
135
-
136
- def parse_accept_options(mime_type, types)
137
- accepts = []
138
- accepts += parse_q_values(types) if types
139
-
140
- if mime_type
141
- if accepts.empty? || accepts.any? { |accept, _| match_mime_type?(mime_type, accept) }
142
- accepts = [[mime_type, 1.0]]
143
- else
144
- return []
192
+ candidate = HTTPUtils.find_best_q_match(accepts, candidates) do |c, matcher|
193
+ match_mime_type?(c[:type] || "application/octet-stream", matcher)
145
194
  end
195
+ return candidate[:filename], candidate[:type], deps, candidate[:index_alias] if candidate
146
196
  end
147
197
 
148
- if accepts.empty?
149
- accepts << ['*/*', 1.0]
150
- end
151
-
152
- accepts
198
+ return nil, nil, deps
153
199
  end
154
200
 
155
- def path_matches(load_path, logical_name, logical_basename)
201
+ # Internal: Finds candidate files on a given path
202
+ #
203
+ # load_path - String. An absolute path to a directory
204
+ # logical_name - String. A filename without extension
205
+ # e.g. "application" or "coffee/foo"
206
+ # mime_exts - Hash of file extensions and their mime types
207
+ # e.g. {".xml.builder"=>"application/xml+builder"}
208
+ #
209
+ # Finds files that match a given `logical_name` with an acceptable
210
+ # mime type that is included in `mime_exts` on the `load_path`.
211
+ #
212
+ # Returns Array. First element is an Array of hashes or empty, second is a String
213
+ def resolve_main_under_path(load_path, logical_name, mime_exts)
156
214
  dirname = File.dirname(File.join(load_path, logical_name))
157
- candidates = dirname_matches(dirname, logical_basename)
158
- deps = file_digest_dependency_set(dirname)
159
-
160
- result = resolve_alternates(load_path, logical_name)
161
- result[0].each do |fn|
162
- candidates << [fn, parse_path_extnames(fn)[1]]
215
+ candidates = self.find_matching_path_for_extensions(dirname, File.basename(logical_name), mime_exts)
216
+ candidates.map! do |c|
217
+ { filename: c[0], type: c[1] }
163
218
  end
164
- deps.merge(result[1])
219
+ return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
220
+ end
221
+
165
222
 
223
+ # Internal: Finds candidate index files in a given path
224
+ #
225
+ # load_path - String. An absolute path to a directory
226
+ # logical_name - String. A filename without extension
227
+ # e.g. "application" or "coffee/foo"
228
+ # mime_exts - Hash of file extensions and their mime types
229
+ # e.g. {".xml.builder"=>"application/xml+builder"}
230
+ #
231
+ # Looking in the given `load_path` this method will find all files under the `logical_name` directory
232
+ # that are named `index` and have a matching mime type in `mime_exts`.
233
+ #
234
+ # Returns Array. First element is an Array of hashes or empty, second is a String
235
+ def resolve_index_under_path(load_path, logical_name, mime_exts)
166
236
  dirname = File.join(load_path, logical_name)
167
- if directory? dirname
168
- result = dirname_matches(dirname, "index")
169
- candidates.concat(result)
237
+
238
+ if self.directory?(dirname)
239
+ candidates = self.find_matching_path_for_extensions(dirname, "index".freeze, mime_exts)
240
+ else
241
+ candidates = []
170
242
  end
171
243
 
172
- deps.merge(file_digest_dependency_set(dirname))
244
+ candidates.map! do |c|
245
+ { filename: c[0],
246
+ type: c[1],
247
+ index_alias: compress_from_root(c[0].sub(/\/index(\.[^\/]+)$/, '\1')) }
248
+ end
173
249
 
174
- return candidates.select { |fn, _| file?(fn) }, deps
250
+ return candidates, [ URIUtils.build_file_digest_uri(dirname) ]
175
251
  end
176
252
 
177
- def dirname_matches(dirname, basename)
178
- candidates = []
179
- entries = self.entries(dirname)
180
- entries.each do |entry|
181
- next unless File.basename(entry).start_with?(basename)
182
- name, type, _, _ = parse_path_extnames(entry)
183
- if basename == name
184
- candidates << [File.join(dirname, entry), type]
185
- end
253
+ def resolve_alts_under_path(load_path, logical_name, mime_exts)
254
+ filenames, deps = self.resolve_alternates(load_path, logical_name)
255
+ filenames.map! do |fn|
256
+ _, mime_type = PathUtils.match_path_extname(fn, mime_exts)
257
+ { filename: fn, type: mime_type }
186
258
  end
187
- candidates
259
+ return filenames, deps
188
260
  end
189
261
 
190
- def resolve_alternates(load_path, logical_name)
191
- return [], Set.new
192
- end
193
-
194
- # Internal: Returns the name, mime type and `Array` of engine extensions.
262
+ # Internal: Converts mimetype into accept Array
195
263
  #
196
- # "foo.js.coffee.erb"
197
- # # => ["foo", "application/javascript", [".coffee", ".erb"]]
264
+ # - mime_type - String, optional. e.g. "text/html"
265
+ # - explicit_type - String, optional. e.g. "application/javascript"
198
266
  #
199
- def parse_path_extnames(path)
200
- engines = []
201
- extname, value = match_path_extname(path, extname_map)
202
-
203
- if extname
204
- path = path.chomp(extname)
205
- type, engines, pipeline = value.values_at(:type, :engines, :pipeline)
267
+ # When called with an explicit_type and a mime_type, only a mime_type
268
+ # that matches the given explicit_type will be accepted.
269
+ #
270
+ # Returns Array of Array
271
+ #
272
+ # [["application/javascript", 1.0]]
273
+ # [["*/*", 1.0]]
274
+ # []
275
+ def parse_accept_options(mime_type, explicit_type)
276
+ if mime_type
277
+ return [[mime_type, 1.0]] if explicit_type.nil?
278
+ return [[mime_type, 1.0]] if HTTPUtils.parse_q_values(explicit_type).any? { |accept, _| HTTPUtils.match_mime_type?(mime_type, accept) }
279
+ return []
206
280
  end
207
281
 
208
- return path, type, engines, pipeline
282
+ accepts = HTTPUtils.parse_q_values(explicit_type)
283
+ accepts << ['*/*'.freeze, 1.0] if accepts.empty?
284
+ return accepts
285
+ end
286
+
287
+ def resolve_alternates(load_path, logical_name)
288
+ return [], Set.new
209
289
  end
210
290
  end
211
291
  end