sprockets 4.0.1

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 (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +72 -0
  3. data/README.md +665 -0
  4. data/bin/sprockets +93 -0
  5. data/lib/rake/sprocketstask.rb +153 -0
  6. data/lib/sprockets.rb +229 -0
  7. data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
  8. data/lib/sprockets/asset.rb +202 -0
  9. data/lib/sprockets/autoload.rb +16 -0
  10. data/lib/sprockets/autoload/babel.rb +8 -0
  11. data/lib/sprockets/autoload/closure.rb +8 -0
  12. data/lib/sprockets/autoload/coffee_script.rb +8 -0
  13. data/lib/sprockets/autoload/eco.rb +8 -0
  14. data/lib/sprockets/autoload/ejs.rb +8 -0
  15. data/lib/sprockets/autoload/jsminc.rb +8 -0
  16. data/lib/sprockets/autoload/sass.rb +8 -0
  17. data/lib/sprockets/autoload/sassc.rb +8 -0
  18. data/lib/sprockets/autoload/uglifier.rb +8 -0
  19. data/lib/sprockets/autoload/yui.rb +8 -0
  20. data/lib/sprockets/autoload/zopfli.rb +7 -0
  21. data/lib/sprockets/babel_processor.rb +66 -0
  22. data/lib/sprockets/base.rb +147 -0
  23. data/lib/sprockets/bower.rb +61 -0
  24. data/lib/sprockets/bundle.rb +105 -0
  25. data/lib/sprockets/cache.rb +271 -0
  26. data/lib/sprockets/cache/file_store.rb +208 -0
  27. data/lib/sprockets/cache/memory_store.rb +75 -0
  28. data/lib/sprockets/cache/null_store.rb +54 -0
  29. data/lib/sprockets/cached_environment.rb +64 -0
  30. data/lib/sprockets/closure_compressor.rb +48 -0
  31. data/lib/sprockets/coffee_script_processor.rb +39 -0
  32. data/lib/sprockets/compressing.rb +134 -0
  33. data/lib/sprockets/configuration.rb +79 -0
  34. data/lib/sprockets/context.rb +304 -0
  35. data/lib/sprockets/dependencies.rb +74 -0
  36. data/lib/sprockets/digest_utils.rb +200 -0
  37. data/lib/sprockets/directive_processor.rb +414 -0
  38. data/lib/sprockets/eco_processor.rb +33 -0
  39. data/lib/sprockets/ejs_processor.rb +32 -0
  40. data/lib/sprockets/encoding_utils.rb +262 -0
  41. data/lib/sprockets/environment.rb +46 -0
  42. data/lib/sprockets/erb_processor.rb +37 -0
  43. data/lib/sprockets/errors.rb +12 -0
  44. data/lib/sprockets/exporters/base.rb +71 -0
  45. data/lib/sprockets/exporters/file_exporter.rb +24 -0
  46. data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
  47. data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
  48. data/lib/sprockets/exporting.rb +73 -0
  49. data/lib/sprockets/file_reader.rb +16 -0
  50. data/lib/sprockets/http_utils.rb +135 -0
  51. data/lib/sprockets/jsminc_compressor.rb +32 -0
  52. data/lib/sprockets/jst_processor.rb +50 -0
  53. data/lib/sprockets/loader.rb +345 -0
  54. data/lib/sprockets/manifest.rb +338 -0
  55. data/lib/sprockets/manifest_utils.rb +48 -0
  56. data/lib/sprockets/mime.rb +96 -0
  57. data/lib/sprockets/npm.rb +52 -0
  58. data/lib/sprockets/path_dependency_utils.rb +77 -0
  59. data/lib/sprockets/path_digest_utils.rb +48 -0
  60. data/lib/sprockets/path_utils.rb +367 -0
  61. data/lib/sprockets/paths.rb +82 -0
  62. data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
  63. data/lib/sprockets/processing.rb +228 -0
  64. data/lib/sprockets/processor_utils.rb +169 -0
  65. data/lib/sprockets/resolve.rb +295 -0
  66. data/lib/sprockets/sass_cache_store.rb +30 -0
  67. data/lib/sprockets/sass_compressor.rb +63 -0
  68. data/lib/sprockets/sass_functions.rb +3 -0
  69. data/lib/sprockets/sass_importer.rb +3 -0
  70. data/lib/sprockets/sass_processor.rb +313 -0
  71. data/lib/sprockets/sassc_compressor.rb +56 -0
  72. data/lib/sprockets/sassc_processor.rb +297 -0
  73. data/lib/sprockets/server.rb +295 -0
  74. data/lib/sprockets/source_map_processor.rb +66 -0
  75. data/lib/sprockets/source_map_utils.rb +483 -0
  76. data/lib/sprockets/transformers.rb +173 -0
  77. data/lib/sprockets/uglifier_compressor.rb +66 -0
  78. data/lib/sprockets/unloaded_asset.rb +139 -0
  79. data/lib/sprockets/uri_tar.rb +99 -0
  80. data/lib/sprockets/uri_utils.rb +191 -0
  81. data/lib/sprockets/utils.rb +202 -0
  82. data/lib/sprockets/utils/gzip.rb +99 -0
  83. data/lib/sprockets/version.rb +4 -0
  84. data/lib/sprockets/yui_compressor.rb +56 -0
  85. metadata +444 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+
4
+ module Sprockets
5
+ module Npm
6
+ # Internal: Override resolve_alternates to install package.json behavior.
7
+ #
8
+ # load_path - String environment path
9
+ # logical_path - String path relative to base
10
+ #
11
+ # Returns candiate filenames.
12
+ def resolve_alternates(load_path, logical_path)
13
+ candidates, deps = super
14
+
15
+ dirname = File.join(load_path, logical_path)
16
+
17
+ if directory?(dirname)
18
+ filename = File.join(dirname, 'package.json')
19
+
20
+ if self.file?(filename)
21
+ deps << build_file_digest_uri(filename)
22
+ read_package_directives(dirname, filename) do |path|
23
+ if file?(path)
24
+ candidates << path
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ return candidates, deps
31
+ end
32
+
33
+ # Internal: Read package.json's main and style directives.
34
+ #
35
+ # dirname - String path to component directory.
36
+ # filename - String path to package.json.
37
+ #
38
+ # Returns nothing.
39
+ def read_package_directives(dirname, filename)
40
+ package = JSON.parse(File.read(filename), create_additions: false)
41
+
42
+ case package['main']
43
+ when String
44
+ yield File.expand_path(package['main'], dirname)
45
+ when nil
46
+ yield File.expand_path('index.js', dirname)
47
+ end
48
+
49
+ yield File.expand_path(package['style'], dirname) if package['style']
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+ require 'set'
3
+ require 'sprockets/path_utils'
4
+ require 'sprockets/uri_utils'
5
+
6
+ module Sprockets
7
+ # Internal: Related PathUtils helpers that also track all the file system
8
+ # calls they make for caching purposes. All functions return a standard
9
+ # return value and a Set of cache dependency URIs that can be used in the
10
+ # future to see if the returned value should be invalidated from cache.
11
+ #
12
+ # entries_with_dependencies("app/assets/javascripts")
13
+ # # => [
14
+ # # ["application.js", "projects.js", "users.js", ...]
15
+ # # #<Set: {"file-digest:/path/to/app/assets/javascripts"}>
16
+ # # ]
17
+ #
18
+ # The returned dependency set can be passed to resolve_dependencies(deps)
19
+ # to check if the returned result is still fresh. In this case, entry always
20
+ # returns a single path, but multiple calls should accumulate dependencies
21
+ # into a single set thats saved off and checked later.
22
+ #
23
+ # resolve_dependencies(deps)
24
+ # # => "\x01\x02\x03"
25
+ #
26
+ # Later, resolving the same set again will produce a different hash if
27
+ # something on the file system has changed.
28
+ #
29
+ # resolve_dependencies(deps)
30
+ # # => "\x03\x04\x05"
31
+ #
32
+ module PathDependencyUtils
33
+ include PathUtils
34
+ include URIUtils
35
+
36
+ # Internal: List directory entries and return a set of dependencies that
37
+ # would invalid the cached return result.
38
+ #
39
+ # See PathUtils#entries
40
+ #
41
+ # path - String directory path
42
+ #
43
+ # Returns an Array of entry names and a Set of dependency URIs.
44
+ def entries_with_dependencies(path)
45
+ return entries(path), Set.new([build_file_digest_uri(path)])
46
+ end
47
+
48
+ # Internal: List directory filenames and associated Stats under a
49
+ # directory.
50
+ #
51
+ # See PathUtils#stat_directory
52
+ #
53
+ # dir - A String directory
54
+ #
55
+ # Returns an Array of filenames and a Set of dependency URIs.
56
+ def stat_directory_with_dependencies(dir)
57
+ return stat_directory(dir).to_a, Set.new([build_file_digest_uri(dir)])
58
+ end
59
+
60
+ # Internal: List directory filenames and associated Stats under an entire
61
+ # directory tree.
62
+ #
63
+ # See PathUtils#stat_sorted_tree
64
+ #
65
+ # dir - A String directory
66
+ #
67
+ # Returns an Array of filenames and a Set of dependency URIs.
68
+ def stat_sorted_tree_with_dependencies(dir)
69
+ deps = Set.new([build_file_digest_uri(dir)])
70
+ results = stat_sorted_tree(dir).map do |path, stat|
71
+ deps << build_file_digest_uri(path) if stat.directory?
72
+ [path, stat]
73
+ end
74
+ return results, deps
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require 'sprockets/digest_utils'
3
+ require 'sprockets/path_utils'
4
+
5
+ module Sprockets
6
+ # Internal: Crossover of path and digest utilities functions.
7
+ module PathDigestUtils
8
+ include DigestUtils, PathUtils
9
+
10
+ # Internal: Compute digest for file stat.
11
+ #
12
+ # path - String filename
13
+ # stat - File::Stat
14
+ #
15
+ # Returns String digest bytes.
16
+ def stat_digest(path, stat)
17
+ if stat.directory?
18
+ # If its a directive, digest the list of filenames
19
+ digest_class.digest(self.entries(path).join(','.freeze))
20
+ elsif stat.file?
21
+ # If its a file, digest the contents
22
+ digest_class.file(path.to_s).digest
23
+ else
24
+ raise TypeError, "stat was not a directory or file: #{stat.ftype}"
25
+ end
26
+ end
27
+
28
+ # Internal: Compute digest for path.
29
+ #
30
+ # path - String filename or directory path.
31
+ #
32
+ # Returns String digest bytes or nil.
33
+ def file_digest(path)
34
+ if stat = self.stat(path)
35
+ self.stat_digest(path, stat)
36
+ end
37
+ end
38
+
39
+ # Internal: Compute digest for a set of paths.
40
+ #
41
+ # paths - Array of filename or directory paths.
42
+ #
43
+ # Returns String digest bytes.
44
+ def files_digest(paths)
45
+ self.digest(paths.map { |path| self.file_digest(path) })
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,367 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ # Internal: File and path related utilities. Mixed into Environment.
4
+ #
5
+ # Probably would be called FileUtils, but that causes namespace annoyances
6
+ # when code actually wants to reference ::FileUtils.
7
+ module PathUtils
8
+ extend self
9
+ require 'pathname'
10
+
11
+ # Public: Like `File.stat`.
12
+ #
13
+ # path - String file or directory path
14
+ #
15
+ # Returns nil if the file does not exist.
16
+ def stat(path)
17
+ if File.exist?(path)
18
+ File.stat(path.to_s)
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ # Public: Like `File.file?`.
25
+ #
26
+ # path - String file path.
27
+ #
28
+ # Returns true path exists and is a file.
29
+ def file?(path)
30
+ if stat = self.stat(path)
31
+ stat.file?
32
+ else
33
+ false
34
+ end
35
+ end
36
+
37
+ # Public: Like `File.directory?`.
38
+ #
39
+ # path - String file path.
40
+ #
41
+ # Returns true path exists and is a directory.
42
+ def directory?(path)
43
+ if stat = self.stat(path)
44
+ stat.directory?
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ # Public: A version of `Dir.entries` that filters out `.` files and `~`
51
+ # swap files.
52
+ #
53
+ # path - String directory path
54
+ #
55
+ # Returns an empty `Array` if the directory does not exist.
56
+ def entries(path)
57
+ if File.directory?(path)
58
+ entries = Dir.entries(path, encoding: Encoding.default_internal)
59
+ entries.reject! { |entry|
60
+ entry.start_with?(".".freeze) ||
61
+ (entry.start_with?("#".freeze) && entry.end_with?("#".freeze)) ||
62
+ entry.end_with?("~".freeze)
63
+ }
64
+ entries.sort!
65
+ entries
66
+ else
67
+ []
68
+ end
69
+ end
70
+
71
+ # Public: Check if path is absolute or relative.
72
+ #
73
+ # path - String path.
74
+ #
75
+ # Returns true if path is absolute, otherwise false.
76
+ if File::ALT_SEPARATOR
77
+ # On Windows, ALT_SEPARATOR is \
78
+ # Delegate to Pathname since the logic gets complex.
79
+ def absolute_path?(path)
80
+ Pathname.new(path).absolute?
81
+ end
82
+ else
83
+ def absolute_path?(path)
84
+ path.start_with?(File::SEPARATOR)
85
+ end
86
+ end
87
+
88
+ if File::ALT_SEPARATOR
89
+ SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}|#{Regexp.quote(File::ALT_SEPARATOR)}"
90
+ else
91
+ SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}"
92
+ end
93
+
94
+ # Public: Check if path is explicitly relative.
95
+ # Starts with "./" or "../".
96
+ #
97
+ # path - String path.
98
+ #
99
+ # Returns true if path is relative, otherwise false.
100
+ def relative_path?(path)
101
+ path.match?(/^\.\.?($|#{SEPARATOR_PATTERN})/) ? true : false
102
+ end
103
+
104
+ # Public: Get relative path from `start` to `dest`.
105
+ #
106
+ # start - String start path (file or dir)
107
+ # dest - String destination path
108
+ #
109
+ # Returns relative String path from `start` to `dest`
110
+ def relative_path_from(start, dest)
111
+ start, dest = Pathname.new(start), Pathname.new(dest)
112
+ start = start.dirname unless start.directory?
113
+ dest.relative_path_from(start).to_s
114
+ end
115
+
116
+ # Public: Joins path to base path.
117
+ #
118
+ # base - Root path
119
+ # path - Extending path
120
+ #
121
+ # Example
122
+ #
123
+ # join('base/path/', '../file.js')
124
+ # # => 'base/file.js'
125
+ #
126
+ # Returns string path starting from base and ending at path
127
+ def join(base, path)
128
+ (Pathname.new(base) + path).to_s
129
+ end
130
+
131
+ # Public: Sets pipeline for path
132
+ #
133
+ # path - String path
134
+ # extensions - List of file extensions
135
+ # pipeline - Pipeline
136
+ #
137
+ # Examples
138
+ #
139
+ # set_pipeline('path/file.js.erb', config[:mime_exts], config[:pipeline_exts], :source)
140
+ # # => 'path/file.source.js.erb'
141
+ #
142
+ # set_pipeline('path/some.file.source.js.erb', config[:mime_exts], config[:pipeline_exts], :debug)
143
+ # # => 'path/some.file.debug.js.erb'
144
+ #
145
+ # Returns string path with pipeline parsed in
146
+ def set_pipeline(path, mime_exts, pipeline_exts, pipeline)
147
+ extension, _ = match_path_extname(path, mime_exts)
148
+ path.chomp!(extension)
149
+ pipeline_old, _ = match_path_extname(path, pipeline_exts)
150
+ path.chomp!(pipeline_old)
151
+
152
+ "#{path}.#{pipeline}#{extension}"
153
+ end
154
+
155
+ # Internal: Get relative path for root path and subpath.
156
+ #
157
+ # path - String path
158
+ # subpath - String subpath of path
159
+ #
160
+ # Returns relative String path if subpath is a subpath of path, or nil if
161
+ # subpath is outside of path.
162
+ def split_subpath(path, subpath)
163
+ return "" if path == subpath
164
+ path = File.join(path, ''.freeze)
165
+ if subpath.start_with?(path)
166
+ subpath[path.length..-1]
167
+ else
168
+ nil
169
+ end
170
+ end
171
+
172
+ # Internal: Detect root path and base for file in a set of paths.
173
+ #
174
+ # paths - Array of String paths
175
+ # filename - String path of file expected to be in one of the paths.
176
+ #
177
+ # Returns [String root, String path]
178
+ def paths_split(paths, filename)
179
+ paths.each do |path|
180
+ if subpath = split_subpath(path, filename)
181
+ return path, subpath
182
+ end
183
+ end
184
+ nil
185
+ end
186
+
187
+ # Internal: Get path's extensions.
188
+ #
189
+ # path - String
190
+ #
191
+ # Returns an Array of String extnames.
192
+ def path_extnames(path)
193
+ File.basename(path).scan(/\.[^.]+/)
194
+ end
195
+
196
+ # Internal: Match path extnames against available extensions.
197
+ #
198
+ # path - String
199
+ # extensions - Hash of String extnames to values
200
+ #
201
+ # Returns [String extname, Object value] or nil nothing matched.
202
+ def match_path_extname(path, extensions)
203
+ basename = File.basename(path)
204
+
205
+ i = basename.index('.'.freeze)
206
+ while i && i < basename.length - 1
207
+ extname = basename[i..-1]
208
+ if value = extensions[extname]
209
+ return extname, value
210
+ end
211
+
212
+ i = basename.index('.'.freeze, i+1)
213
+ end
214
+
215
+ nil
216
+ end
217
+
218
+ # Internal: Match paths in a directory against available extensions.
219
+ #
220
+ # path - String directory
221
+ # basename - String basename of target file
222
+ # extensions - Hash of String extnames to values
223
+ #
224
+ # Examples
225
+ #
226
+ # exts = { ".js" => "application/javascript" }
227
+ # find_matching_path_for_extensions("app/assets", "application", exts)
228
+ # # => ["app/assets/application.js", "application/javascript"]
229
+ #
230
+ # Returns an Array of [String path, Object value] matches.
231
+ def find_matching_path_for_extensions(path, basename, extensions)
232
+ matches = []
233
+ entries(path).each do |entry|
234
+ next unless File.basename(entry).start_with?(basename)
235
+ extname, value = match_path_extname(entry, extensions)
236
+ if basename == entry.chomp(extname)
237
+ filename = File.join(path, entry)
238
+ if file?(filename)
239
+ matches << [filename, value]
240
+ end
241
+ end
242
+ end
243
+ matches
244
+ end
245
+
246
+ # Internal: Returns all parents for path
247
+ #
248
+ # path - String absolute filename or directory
249
+ # root - String path to stop at (default: system root)
250
+ #
251
+ # Returns an Array of String paths.
252
+ def path_parents(path, root = nil)
253
+ root = "#{root}#{File::SEPARATOR}" if root
254
+ parents = []
255
+
256
+ loop do
257
+ parent = File.dirname(path)
258
+ break if parent == path
259
+ break if root && !path.start_with?(root)
260
+ parents << path = parent
261
+ end
262
+
263
+ parents
264
+ end
265
+
266
+ # Internal: Find target basename checking upwards from path.
267
+ #
268
+ # basename - String filename: ".sprocketsrc"
269
+ # path - String path to start search: "app/assets/javascripts/app.js"
270
+ # root - String path to stop at (default: system root)
271
+ #
272
+ # Returns String filename or nil.
273
+ def find_upwards(basename, path, root = nil)
274
+ path_parents(path, root).each do |dir|
275
+ filename = File.join(dir, basename)
276
+ return filename if file?(filename)
277
+ end
278
+ nil
279
+ end
280
+
281
+ # Public: Stat all the files under a directory.
282
+ #
283
+ # dir - A String directory
284
+ #
285
+ # Returns an Enumerator of [path, stat].
286
+ def stat_directory(dir)
287
+ return to_enum(__method__, dir) unless block_given?
288
+
289
+ self.entries(dir).each do |entry|
290
+ path = File.join(dir, entry)
291
+ if stat = self.stat(path)
292
+ yield path, stat
293
+ end
294
+ end
295
+
296
+ nil
297
+ end
298
+
299
+ # Public: Recursive stat all the files under a directory.
300
+ #
301
+ # dir - A String directory
302
+ #
303
+ # Returns an Enumerator of [path, stat].
304
+ def stat_tree(dir, &block)
305
+ return to_enum(__method__, dir) unless block_given?
306
+
307
+ self.stat_directory(dir) do |path, stat|
308
+ yield path, stat
309
+
310
+ if stat.directory?
311
+ stat_tree(path, &block)
312
+ end
313
+ end
314
+
315
+ nil
316
+ end
317
+
318
+ # Public: Recursive stat all the files under a directory in alphabetical
319
+ # order.
320
+ #
321
+ # dir - A String directory
322
+ #
323
+ # Returns an Enumerator of [path, stat].
324
+ def stat_sorted_tree(dir, &block)
325
+ return to_enum(__method__, dir) unless block_given?
326
+
327
+ self.stat_directory(dir).sort_by { |path, stat|
328
+ stat.directory? ? "#{path}/" : path
329
+ }.each do |path, stat|
330
+ yield path, stat
331
+
332
+ if stat.directory?
333
+ stat_sorted_tree(path, &block)
334
+ end
335
+ end
336
+
337
+ nil
338
+ end
339
+
340
+ # Public: Write to a file atomically. Useful for situations where you
341
+ # don't want other processes or threads to see half-written files.
342
+ #
343
+ # Utils.atomic_write('important.file') do |file|
344
+ # file.write('hello')
345
+ # end
346
+ #
347
+ # Returns nothing.
348
+ def atomic_write(filename)
349
+ dirname, basename = File.split(filename)
350
+ basename = [
351
+ basename,
352
+ Thread.current.object_id,
353
+ Process.pid,
354
+ rand(1000000)
355
+ ].join('.'.freeze)
356
+ tmpname = File.join(dirname, basename)
357
+
358
+ File.open(tmpname, 'wb+') do |f|
359
+ yield f
360
+ end
361
+
362
+ File.rename(tmpname, filename)
363
+ ensure
364
+ File.delete(tmpname) if File.exist?(tmpname)
365
+ end
366
+ end
367
+ end