sprockets 2.2.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +68 -0
  3. data/README.md +482 -255
  4. data/bin/sprockets +20 -7
  5. data/lib/rake/sprocketstask.rb +28 -15
  6. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  7. data/lib/sprockets/asset.rb +142 -207
  8. data/lib/sprockets/autoload/babel.rb +8 -0
  9. data/lib/sprockets/autoload/closure.rb +8 -0
  10. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  11. data/lib/sprockets/autoload/eco.rb +8 -0
  12. data/lib/sprockets/autoload/ejs.rb +8 -0
  13. data/lib/sprockets/autoload/jsminc.rb +8 -0
  14. data/lib/sprockets/autoload/sass.rb +8 -0
  15. data/lib/sprockets/autoload/sassc.rb +8 -0
  16. data/lib/sprockets/autoload/uglifier.rb +8 -0
  17. data/lib/sprockets/autoload/yui.rb +8 -0
  18. data/lib/sprockets/autoload/zopfli.rb +7 -0
  19. data/lib/sprockets/autoload.rb +16 -0
  20. data/lib/sprockets/babel_processor.rb +66 -0
  21. data/lib/sprockets/base.rb +89 -249
  22. data/lib/sprockets/bower.rb +61 -0
  23. data/lib/sprockets/bundle.rb +105 -0
  24. data/lib/sprockets/cache/file_store.rb +190 -14
  25. data/lib/sprockets/cache/memory_store.rb +75 -0
  26. data/lib/sprockets/cache/null_store.rb +54 -0
  27. data/lib/sprockets/cache.rb +271 -0
  28. data/lib/sprockets/cached_environment.rb +64 -0
  29. data/lib/sprockets/closure_compressor.rb +48 -0
  30. data/lib/sprockets/coffee_script_processor.rb +39 -0
  31. data/lib/sprockets/compressing.rb +134 -0
  32. data/lib/sprockets/configuration.rb +79 -0
  33. data/lib/sprockets/context.rb +204 -135
  34. data/lib/sprockets/dependencies.rb +74 -0
  35. data/lib/sprockets/digest_utils.rb +200 -0
  36. data/lib/sprockets/directive_processor.rb +224 -216
  37. data/lib/sprockets/eco_processor.rb +33 -0
  38. data/lib/sprockets/ejs_processor.rb +32 -0
  39. data/lib/sprockets/encoding_utils.rb +262 -0
  40. data/lib/sprockets/environment.rb +23 -68
  41. data/lib/sprockets/erb_processor.rb +37 -0
  42. data/lib/sprockets/errors.rb +6 -13
  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 +16 -0
  49. data/lib/sprockets/http_utils.rb +135 -0
  50. data/lib/sprockets/jsminc_compressor.rb +32 -0
  51. data/lib/sprockets/jst_processor.rb +36 -19
  52. data/lib/sprockets/loader.rb +343 -0
  53. data/lib/sprockets/manifest.rb +231 -96
  54. data/lib/sprockets/manifest_utils.rb +48 -0
  55. data/lib/sprockets/mime.rb +80 -32
  56. data/lib/sprockets/npm.rb +52 -0
  57. data/lib/sprockets/path_dependency_utils.rb +77 -0
  58. data/lib/sprockets/path_digest_utils.rb +48 -0
  59. data/lib/sprockets/path_utils.rb +367 -0
  60. data/lib/sprockets/paths.rb +82 -0
  61. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  62. data/lib/sprockets/processing.rb +140 -192
  63. data/lib/sprockets/processor_utils.rb +169 -0
  64. data/lib/sprockets/resolve.rb +295 -0
  65. data/lib/sprockets/sass_cache_store.rb +30 -0
  66. data/lib/sprockets/sass_compressor.rb +63 -0
  67. data/lib/sprockets/sass_functions.rb +3 -0
  68. data/lib/sprockets/sass_importer.rb +3 -0
  69. data/lib/sprockets/sass_processor.rb +313 -0
  70. data/lib/sprockets/sassc_compressor.rb +56 -0
  71. data/lib/sprockets/sassc_processor.rb +297 -0
  72. data/lib/sprockets/server.rb +138 -90
  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 +173 -0
  76. data/lib/sprockets/uglifier_compressor.rb +66 -0
  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 +191 -0
  80. data/lib/sprockets/utils/gzip.rb +99 -0
  81. data/lib/sprockets/utils.rb +186 -53
  82. data/lib/sprockets/version.rb +2 -1
  83. data/lib/sprockets/yui_compressor.rb +56 -0
  84. data/lib/sprockets.rb +217 -52
  85. metadata +250 -59
  86. data/LICENSE +0 -21
  87. data/lib/sprockets/asset_attributes.rb +0 -126
  88. data/lib/sprockets/bundled_asset.rb +0 -79
  89. data/lib/sprockets/caching.rb +0 -96
  90. data/lib/sprockets/charset_normalizer.rb +0 -41
  91. data/lib/sprockets/eco_template.rb +0 -38
  92. data/lib/sprockets/ejs_template.rb +0 -37
  93. data/lib/sprockets/engines.rb +0 -74
  94. data/lib/sprockets/index.rb +0 -99
  95. data/lib/sprockets/processed_asset.rb +0 -152
  96. data/lib/sprockets/processor.rb +0 -32
  97. data/lib/sprockets/safety_colons.rb +0 -28
  98. data/lib/sprockets/static_asset.rb +0 -57
  99. data/lib/sprockets/trail.rb +0 -90
@@ -1,7 +1,6 @@
1
- require 'pathname'
1
+ # frozen_string_literal: true
2
+ require 'set'
2
3
  require 'shellwords'
3
- require 'tilt'
4
- require 'yaml'
5
4
 
6
5
  module Sprockets
7
6
  # The `DirectiveProcessor` is responsible for parsing and evaluating
@@ -20,10 +19,9 @@ module Sprockets
20
19
  # *= require "baz"
21
20
  # */
22
21
  #
23
- # The Processor is implemented as a `Tilt::Template` and is loosely
24
- # coupled to Sprockets. This makes it possible to disable or modify
25
- # the processor to do whatever you'd like. You could add your own
26
- # custom directives or invent your own directive syntax.
22
+ # This makes it possible to disable or modify the processor to do whatever
23
+ # you'd like. You could add your own custom directives or invent your own
24
+ # directive syntax.
27
25
  #
28
26
  # `Environment#processors` includes `DirectiveProcessor` by default.
29
27
  #
@@ -36,25 +34,7 @@ module Sprockets
36
34
  #
37
35
  # env.register_processor('text/css', MyProcessor)
38
36
  #
39
- class DirectiveProcessor < Tilt::Template
40
- # Directives will only be picked up if they are in the header
41
- # of the source file. C style (/* */), JavaScript (//), and
42
- # Ruby (#) comments are supported.
43
- #
44
- # Directives in comments after the first non-whitespace line
45
- # of code will not be processed.
46
- #
47
- HEADER_PATTERN = /
48
- \A (
49
- (?m:\s*) (
50
- (\/\* (?m:.*?) \*\/) |
51
- (\#\#\# (?m:.*?) \#\#\#) |
52
- (\/\/ .* \n?)+ |
53
- (\# .* \n?)+
54
- )
55
- )+
56
- /x
57
-
37
+ class DirectiveProcessor
58
38
  # Directives are denoted by a `=` followed by the name, then
59
39
  # argument list.
60
40
  #
@@ -65,77 +45,121 @@ module Sprockets
65
45
  # //= require "foo"
66
46
  #
67
47
  DIRECTIVE_PATTERN = /
68
- ^ [\W]* = \s* (\w+.*?) (\*\/)? $
48
+ ^ \W* = \s* (\w+.*?) (\*\/)? $
69
49
  /x
70
50
 
71
- attr_reader :pathname
72
- attr_reader :header, :body
51
+ def self.instance
52
+ # Default to C comment styles
53
+ @instance ||= new(comments: ["//", ["/*", "*/"]])
54
+ end
55
+
56
+ def self.call(input)
57
+ instance.call(input)
58
+ end
73
59
 
74
- def prepare
75
- @pathname = Pathname.new(file)
60
+ def initialize(comments: [])
61
+ @header_pattern = compile_header_pattern(Array(comments))
62
+ end
76
63
 
77
- @header = data[HEADER_PATTERN, 0] || ""
78
- @body = $' || data
79
- # Ensure body ends in a new line
80
- @body += "\n" if @body != "" && @body !~ /\n\Z/m
64
+ def call(input)
65
+ dup._call(input)
66
+ end
81
67
 
82
- @included_pathnames = []
83
- @compat = false
68
+ def _call(input)
69
+ @environment = input[:environment]
70
+ @uri = input[:uri]
71
+ @filename = input[:filename]
72
+ @dirname = File.dirname(@filename)
73
+ # If loading a source map file like `application.js.map` resolve
74
+ # dependencies using `.js` instead of `.js.map`
75
+ @content_type = SourceMapProcessor.original_content_type(input[:content_type], error_when_not_found: false)
76
+ @required = Set.new(input[:metadata][:required])
77
+ @stubbed = Set.new(input[:metadata][:stubbed])
78
+ @links = Set.new(input[:metadata][:links])
79
+ @dependencies = Set.new(input[:metadata][:dependencies])
80
+ @to_link = Set.new
81
+ @to_load = Set.new
82
+
83
+ data, directives = process_source(input[:data])
84
+ process_directives(directives)
85
+
86
+ {
87
+ data: data,
88
+ required: @required,
89
+ stubbed: @stubbed,
90
+ links: @links,
91
+ to_load: @to_load,
92
+ to_link: @to_link,
93
+ dependencies: @dependencies
94
+ }
84
95
  end
85
96
 
86
- # Implemented for Tilt#render.
87
- #
88
- # `context` is a `Context` instance with methods that allow you to
89
- # access the environment and append to the bundle. See `Context`
90
- # for the complete API.
91
- def evaluate(context, locals, &block)
92
- @context = context
97
+ protected
98
+ # Directives will only be picked up if they are in the header
99
+ # of the source file. C style (/* */), JavaScript (//), and
100
+ # Ruby (#) comments are supported.
101
+ #
102
+ # Directives in comments after the first non-whitespace line
103
+ # of code will not be processed.
104
+ def compile_header_pattern(comments)
105
+ re = comments.map { |c|
106
+ case c
107
+ when String
108
+ "(?:#{Regexp.escape(c)}.*\\n?)+"
109
+ when Array
110
+ "(?:#{Regexp.escape(c[0])}(?m:.*?)#{Regexp.escape(c[1])})"
111
+ else
112
+ raise TypeError, "unknown comment type: #{c.class}"
113
+ end
114
+ }.join("|")
115
+ Regexp.compile("\\A(?:(?m:\\s*)(?:#{re}))+")
116
+ end
93
117
 
94
- @result = ""
95
- @has_written_body = false
118
+ def process_source(source)
119
+ header = source[@header_pattern, 0] || ""
120
+ body = $' || source
96
121
 
97
- process_directives
98
- process_source
122
+ header, directives = extract_directives(header)
99
123
 
100
- @result
101
- end
124
+ data = +""
125
+ data.force_encoding(body.encoding)
126
+ data << header unless header.empty?
127
+ data << body
128
+ # Ensure body ends in a new line
129
+ data << "\n" if data.length > 0 && data[-1] != "\n"
102
130
 
103
- # Returns the header String with any directives stripped.
104
- def processed_header
105
- lineno = 0
106
- @processed_header ||= header.lines.map { |line|
107
- lineno += 1
108
- # Replace directive line with a clean break
109
- directives.assoc(lineno) ? "\n" : line
110
- }.join.chomp
111
- end
131
+ return data, directives
132
+ end
112
133
 
113
- # Returns the source String with any directives stripped.
114
- def processed_source
115
- @processed_source ||= processed_header + body
116
- end
134
+ # Returns an Array of directive structures. Each structure
135
+ # is an Array with the line number as the first element, the
136
+ # directive name as the second element, followed by any
137
+ # arguments.
138
+ #
139
+ # [[1, "require", "foo"], [2, "require", "bar"]]
140
+ #
141
+ def extract_directives(header)
142
+ processed_header = +""
143
+ directives = []
117
144
 
118
- # Returns an Array of directive structures. Each structure
119
- # is an Array with the line number as the first element, the
120
- # directive name as the second element, followed by any
121
- # arguments.
122
- #
123
- # [[1, "require", "foo"], [2, "require", "bar"]]
124
- #
125
- def directives
126
- @directives ||= header.lines.each_with_index.map { |line, index|
127
- if directive = line[DIRECTIVE_PATTERN, 1]
128
- name, *args = Shellwords.shellwords(directive)
129
- if respond_to?("process_#{name}_directive", true)
130
- [index + 1, name, *args]
145
+ header.lines.each_with_index do |line, index|
146
+ if directive = line[DIRECTIVE_PATTERN, 1]
147
+ name, *args = Shellwords.shellwords(directive)
148
+ if respond_to?("process_#{name}_directive", true)
149
+ directives << [index + 1, name, *args]
150
+ # Replace directive line with a clean break
151
+ line = "\n"
152
+ end
131
153
  end
154
+ processed_header << line
132
155
  end
133
- }.compact
134
- end
135
156
 
136
- protected
137
- attr_reader :included_pathnames
138
- attr_reader :context
157
+ processed_header.chomp!
158
+ # Ensure header ends in a new line like before it was processed
159
+ processed_header << "\n" if processed_header.length > 0 && header[-1] == "\n"
160
+
161
+ return processed_header, directives
162
+ end
139
163
 
140
164
  # Gathers comment directives in the source and processes them.
141
165
  # Any directive method matching `process_*_directive` will
@@ -147,8 +171,8 @@ module Sprockets
147
171
  # `process_require_glob_directive`.
148
172
  #
149
173
  # class DirectiveProcessor < Sprockets::DirectiveProcessor
150
- # def process_require_glob_directive
151
- # Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
174
+ # def process_require_glob_directive(glob)
175
+ # Dir["#{dirname}/#{glob}"].sort.each do |filename|
152
176
  # require(filename)
153
177
  # end
154
178
  # end
@@ -159,35 +183,20 @@ module Sprockets
159
183
  # env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
160
184
  # env.register_processor('text/css', DirectiveProcessor)
161
185
  #
162
- def process_directives
186
+ def process_directives(directives)
163
187
  directives.each do |line_number, name, *args|
164
- context.__LINE__ = line_number
165
- send("process_#{name}_directive", *args)
166
- context.__LINE__ = nil
167
- end
168
- end
169
-
170
- def process_source
171
- unless @has_written_body || processed_header.empty?
172
- @result << processed_header << "\n"
173
- end
174
-
175
- included_pathnames.each do |pathname|
176
- @result << context.evaluate(pathname)
177
- end
178
-
179
- unless @has_written_body
180
- @result << body
181
- end
182
-
183
- if compat? && constants.any?
184
- @result.gsub!(/<%=(.*?)%>/) { constants[$1.strip] }
188
+ begin
189
+ send("process_#{name}_directive", *args)
190
+ rescue Exception => e
191
+ e.set_backtrace(["#{@filename}:#{line_number}"] + e.backtrace)
192
+ raise e
193
+ end
185
194
  end
186
195
  end
187
196
 
188
197
  # The `require` directive functions similar to Ruby's own `require`.
189
198
  # It provides a way to declare a dependency on a file in your path
190
- # and ensures its only loaded once before the source file.
199
+ # and ensures it's only loaded once before the source file.
191
200
  #
192
201
  # `require` works with files in the environment path:
193
202
  #
@@ -204,22 +213,13 @@ module Sprockets
204
213
  # //= require "./bar"
205
214
  #
206
215
  def process_require_directive(path)
207
- if @compat
208
- if path =~ /<([^>]+)>/
209
- path = $1
210
- else
211
- path = "./#{path}" unless relative?(path)
212
- end
213
- end
214
-
215
- context.require_asset(path)
216
+ @required << resolve(path, accept: @content_type, pipeline: :self)
216
217
  end
217
218
 
218
- # `require_self` causes the body of the current file to be
219
- # inserted before any subsequent `require` or `include`
220
- # directives. Useful in CSS files, where it's common for the
221
- # index file to contain global styles that need to be defined
222
- # before other dependencies are loaded.
219
+ # `require_self` causes the body of the current file to be inserted
220
+ # before any subsequent `require` directives. Useful in CSS files, where
221
+ # it's common for the index file to contain global styles that need to
222
+ # be defined before other dependencies are loaded.
223
223
  #
224
224
  # /*= require "reset"
225
225
  # *= require_self
@@ -227,26 +227,10 @@ module Sprockets
227
227
  # */
228
228
  #
229
229
  def process_require_self_directive
230
- if @has_written_body
230
+ if @required.include?(@uri)
231
231
  raise ArgumentError, "require_self can only be called once per source file"
232
232
  end
233
-
234
- context.require_asset(pathname)
235
- process_source
236
- included_pathnames.clear
237
- @has_written_body = true
238
- end
239
-
240
- # The `include` directive works similar to `require` but
241
- # inserts the contents of the dependency even if it already
242
- # has been required.
243
- #
244
- # //= include "header"
245
- #
246
- def process_include_directive(path)
247
- pathname = context.resolve(path)
248
- context.depend_on_asset(pathname)
249
- included_pathnames << pathname
233
+ @required << @uri
250
234
  end
251
235
 
252
236
  # `require_directory` requires all the files inside a single
@@ -256,27 +240,8 @@ module Sprockets
256
240
  # //= require_directory "./javascripts"
257
241
  #
258
242
  def process_require_directory_directive(path = ".")
259
- if relative?(path)
260
- root = pathname.dirname.join(path).expand_path
261
-
262
- unless (stats = stat(root)) && stats.directory?
263
- raise ArgumentError, "require_directory argument must be a directory"
264
- end
265
-
266
- context.depend_on(root)
267
-
268
- entries(root).each do |pathname|
269
- pathname = root.join(pathname)
270
- if pathname.to_s == self.file
271
- next
272
- elsif context.asset_requirable?(pathname)
273
- context.require_asset(pathname)
274
- end
275
- end
276
- else
277
- # The path must be relative and start with a `./`.
278
- raise ArgumentError, "require_directory argument must be a relative path"
279
- end
243
+ path = expand_relative_dirname(:require_directory, path)
244
+ require_paths(*@environment.stat_directory_with_dependencies(path))
280
245
  end
281
246
 
282
247
  # `require_tree` requires all the nested files in a directory.
@@ -285,28 +250,8 @@ module Sprockets
285
250
  # //= require_tree "./public"
286
251
  #
287
252
  def process_require_tree_directive(path = ".")
288
- if relative?(path)
289
- root = pathname.dirname.join(path).expand_path
290
-
291
- unless (stats = stat(root)) && stats.directory?
292
- raise ArgumentError, "require_tree argument must be a directory"
293
- end
294
-
295
- context.depend_on(root)
296
-
297
- each_entry(root) do |pathname|
298
- if pathname.to_s == self.file
299
- next
300
- elsif stat(pathname).directory?
301
- context.depend_on(pathname)
302
- elsif context.asset_requirable?(pathname)
303
- context.require_asset(pathname)
304
- end
305
- end
306
- else
307
- # The path must be relative and start with a `./`.
308
- raise ArgumentError, "require_tree argument must be a relative path"
309
- end
253
+ path = expand_relative_dirname(:require_tree, path)
254
+ require_paths(*@environment.stat_sorted_tree_with_dependencies(path))
310
255
  end
311
256
 
312
257
  # Allows you to state a dependency on a file without
@@ -322,22 +267,22 @@ module Sprockets
322
267
  # //= depend_on "foo.png"
323
268
  #
324
269
  def process_depend_on_directive(path)
325
- context.depend_on(path)
270
+ resolve(path)
326
271
  end
327
272
 
328
273
  # Allows you to state a dependency on an asset without including
329
274
  # it.
330
275
  #
331
276
  # This is used for caching purposes. Any changes that would
332
- # invalid the asset dependency will invalidate the cache our the
333
- # source file.
277
+ # invalidate the asset dependency will invalidate the cache of
278
+ # the source file.
334
279
  #
335
280
  # Unlike `depend_on`, the path must be a requirable asset.
336
281
  #
337
282
  # //= depend_on_asset "bar.js"
338
283
  #
339
284
  def process_depend_on_asset_directive(path)
340
- context.depend_on_asset(path)
285
+ to_load(resolve(path))
341
286
  end
342
287
 
343
288
  # Allows dependency to be excluded from the asset bundle.
@@ -349,58 +294,121 @@ module Sprockets
349
294
  # //= stub "jquery"
350
295
  #
351
296
  def process_stub_directive(path)
352
- context.stub_asset(path)
297
+ @stubbed << resolve(path, accept: @content_type, pipeline: :self)
298
+ end
299
+
300
+ # Declares a linked dependency on the target asset.
301
+ #
302
+ # The `path` must be a valid asset and should not already be part of the
303
+ # bundle. Any linked assets will automatically be compiled along with the
304
+ # current.
305
+ #
306
+ # /*= link "logo.png" */
307
+ #
308
+ def process_link_directive(path)
309
+ uri = to_load(resolve(path))
310
+ @to_link << uri
353
311
  end
354
312
 
355
- # Enable Sprockets 1.x compat mode.
313
+ # `link_directory` links all the files inside a single
314
+ # directory. It's similar to `path/*` since it does not follow
315
+ # nested directories.
316
+ #
317
+ # //= link_directory "./fonts"
356
318
  #
357
- # Makes it possible to use the same JavaScript source
358
- # file in both Sprockets 1 and 2.
319
+ # Use caution when linking against JS or CSS assets. Include an explicit
320
+ # extension or content type in these cases.
359
321
  #
360
- # //= compat
322
+ # //= link_directory "./scripts" .js
361
323
  #
362
- def process_compat_directive
363
- @compat = true
324
+ def process_link_directory_directive(path = ".", accept = nil)
325
+ path = expand_relative_dirname(:link_directory, path)
326
+ accept = expand_accept_shorthand(accept)
327
+ link_paths(*@environment.stat_directory_with_dependencies(path), accept)
364
328
  end
365
329
 
366
- # Checks if Sprockets 1.x compat mode enabled
367
- def compat?
368
- @compat
330
+ # `link_tree` links all the nested files in a directory.
331
+ # Its glob equivalent is `path/**/*`.
332
+ #
333
+ # //= link_tree "./images"
334
+ #
335
+ # Use caution when linking against JS or CSS assets. Include an explicit
336
+ # extension or content type in these cases.
337
+ #
338
+ # //= link_tree "./styles" .css
339
+ #
340
+ def process_link_tree_directive(path = ".", accept = nil)
341
+ path = expand_relative_dirname(:link_tree, path)
342
+ accept = expand_accept_shorthand(accept)
343
+ link_paths(*@environment.stat_sorted_tree_with_dependencies(path), accept)
369
344
  end
370
345
 
371
- # Sprockets 1.x allowed for constant interpolation if a
372
- # constants.yml was present. This is only available if
373
- # compat mode is on.
374
- def constants
375
- if compat?
376
- pathname = Pathname.new(context.root_path).join("constants.yml")
377
- stat(pathname) ? YAML.load_file(pathname) : {}
346
+ private
347
+ def expand_accept_shorthand(accept)
348
+ if accept.nil?
349
+ nil
350
+ elsif accept.include?("/")
351
+ accept
352
+ elsif accept.start_with?(".")
353
+ @environment.mime_exts[accept]
378
354
  else
379
- {}
355
+ @environment.mime_exts[".#{accept}"]
380
356
  end
381
357
  end
382
358
 
383
- # `provide` is stubbed out for Sprockets 1.x compat.
384
- # Mutating the path when an asset is being built is
385
- # not permitted.
386
- def process_provide_directive(path)
359
+ def require_paths(paths, deps)
360
+ resolve_paths(paths, deps, accept: @content_type, pipeline: :self) do |uri|
361
+ @required << uri
362
+ end
387
363
  end
388
364
 
389
- private
390
- def relative?(path)
391
- path =~ /^\.($|\.?\/)/
365
+ def link_paths(paths, deps, accept)
366
+ resolve_paths(paths, deps, accept: accept) do |uri|
367
+ @to_link << to_load(uri)
368
+ end
392
369
  end
393
370
 
394
- def stat(path)
395
- context.environment.stat(path)
371
+ def resolve_paths(paths, deps, **kargs)
372
+ @dependencies.merge(deps)
373
+ paths.each do |subpath, stat|
374
+ next if subpath == @filename || stat.directory?
375
+ uri, deps = @environment.resolve(subpath, **kargs)
376
+ @dependencies.merge(deps)
377
+ yield uri if uri
378
+ end
396
379
  end
397
380
 
398
- def entries(path)
399
- context.environment.entries(path)
381
+ def expand_relative_dirname(directive, path)
382
+ if @environment.relative_path?(path)
383
+ path = File.expand_path(path, @dirname)
384
+ stat = @environment.stat(path)
385
+
386
+ if stat && stat.directory?
387
+ path
388
+ else
389
+ raise ArgumentError, "#{directive} argument must be a directory"
390
+ end
391
+ else
392
+ # The path must be relative and start with a `./`.
393
+ raise ArgumentError, "#{directive} argument must be a relative path"
394
+ end
395
+ end
396
+
397
+ def to_load(uri)
398
+ @to_load << uri
399
+ uri
400
400
  end
401
401
 
402
- def each_entry(root, &block)
403
- context.environment.each_entry(root, &block)
402
+ def resolve(path, **kargs)
403
+ # Prevent absolute paths in directives
404
+ if @environment.absolute_path?(path)
405
+ raise FileOutsidePaths, "can't require absolute file: #{path}"
406
+ end
407
+
408
+ kargs[:base_path] = @dirname
409
+ uri, deps = @environment.resolve!(path, **kargs)
410
+ @dependencies.merge(deps)
411
+ uri
404
412
  end
405
413
  end
406
414
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+
4
+ module Sprockets
5
+ # Processor engine class for the Eco compiler. Depends on the `eco` gem.
6
+ #
7
+ # For more infomation see:
8
+ #
9
+ # https://github.com/sstephenson/ruby-eco
10
+ # https://github.com/sstephenson/eco
11
+ #
12
+ module EcoProcessor
13
+ VERSION = '1'
14
+
15
+ def self.cache_key
16
+ @cache_key ||= "#{name}:#{Autoload::Eco::Source::VERSION}:#{VERSION}".freeze
17
+ end
18
+
19
+ # Compile template data with Eco compiler.
20
+ #
21
+ # Returns a JS function definition String. The result should be
22
+ # assigned to a JS variable.
23
+ #
24
+ # # => "function(...) {...}"
25
+ #
26
+ def self.call(input)
27
+ data = input[:data]
28
+ input[:cache].fetch([cache_key, data]) do
29
+ Autoload::Eco.compile(data)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/autoload'
3
+
4
+ module Sprockets
5
+ # Processor engine class for the EJS compiler. Depends on the `ejs` gem.
6
+ #
7
+ # For more infomation see:
8
+ #
9
+ # https://github.com/sstephenson/ruby-ejs
10
+ #
11
+ module EjsProcessor
12
+ VERSION = '1'
13
+
14
+ def self.cache_key
15
+ @cache_key ||= "#{name}:#{VERSION}".freeze
16
+ end
17
+
18
+ # Compile template data with EJS compiler.
19
+ #
20
+ # Returns a JS function definition String. The result should be
21
+ # assigned to a JS variable.
22
+ #
23
+ # # => "function(obj){...}"
24
+ #
25
+ def self.call(input)
26
+ data = input[:data]
27
+ input[:cache].fetch([cache_key, data]) do
28
+ Autoload::EJS.compile(data)
29
+ end
30
+ end
31
+ end
32
+ end