sprockets 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
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