sprockets 3.0.3 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +101 -0
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +531 -276
- data/bin/sprockets +12 -7
- data/lib/rake/sprocketstask.rb +9 -4
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +41 -28
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +1 -0
- data/lib/sprockets/autoload/coffee_script.rb +1 -0
- data/lib/sprockets/autoload/eco.rb +1 -0
- data/lib/sprockets/autoload/ejs.rb +1 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +1 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +1 -0
- data/lib/sprockets/autoload/yui.rb +1 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/autoload.rb +5 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +61 -13
- data/lib/sprockets/bower.rb +6 -3
- data/lib/sprockets/bundle.rb +41 -5
- data/lib/sprockets/cache/file_store.rb +32 -7
- data/lib/sprockets/cache/memory_store.rb +28 -10
- data/lib/sprockets/cache/null_store.rb +8 -0
- data/lib/sprockets/cache.rb +43 -6
- data/lib/sprockets/cached_environment.rb +15 -20
- data/lib/sprockets/closure_compressor.rb +6 -11
- data/lib/sprockets/coffee_script_processor.rb +20 -6
- data/lib/sprockets/compressing.rb +62 -2
- data/lib/sprockets/configuration.rb +5 -9
- data/lib/sprockets/context.rb +99 -25
- data/lib/sprockets/dependencies.rb +10 -9
- data/lib/sprockets/digest_utils.rb +103 -62
- data/lib/sprockets/directive_processor.rb +64 -36
- data/lib/sprockets/eco_processor.rb +4 -3
- data/lib/sprockets/ejs_processor.rb +4 -3
- data/lib/sprockets/encoding_utils.rb +1 -0
- data/lib/sprockets/environment.rb +9 -4
- data/lib/sprockets/erb_processor.rb +34 -21
- data/lib/sprockets/errors.rb +1 -0
- data/lib/sprockets/exporters/base.rb +71 -0
- data/lib/sprockets/exporters/file_exporter.rb +24 -0
- data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
- data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
- data/lib/sprockets/exporting.rb +73 -0
- data/lib/sprockets/file_reader.rb +1 -0
- data/lib/sprockets/http_utils.rb +25 -7
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +11 -10
- data/lib/sprockets/loader.rb +244 -62
- data/lib/sprockets/manifest.rb +100 -46
- data/lib/sprockets/manifest_utils.rb +9 -6
- data/lib/sprockets/mime.rb +8 -42
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +3 -11
- data/lib/sprockets/path_digest_utils.rb +2 -1
- data/lib/sprockets/path_utils.rb +107 -22
- data/lib/sprockets/paths.rb +1 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +32 -52
- data/lib/sprockets/processor_utils.rb +38 -39
- data/lib/sprockets/resolve.rb +177 -97
- data/lib/sprockets/sass_cache_store.rb +1 -0
- data/lib/sprockets/sass_compressor.rb +21 -17
- data/lib/sprockets/sass_functions.rb +1 -0
- data/lib/sprockets/sass_importer.rb +1 -0
- data/lib/sprockets/sass_processor.rb +46 -18
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +77 -44
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +63 -35
- data/lib/sprockets/uglifier_compressor.rb +23 -20
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +14 -14
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +63 -71
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +5 -14
- data/lib/sprockets.rb +105 -33
- metadata +157 -27
- data/lib/sprockets/coffee_script_template.rb +0 -6
- data/lib/sprockets/eco_template.rb +0 -6
- data/lib/sprockets/ejs_template.rb +0 -6
- data/lib/sprockets/engines.rb +0 -81
- data/lib/sprockets/erb_template.rb +0 -6
- data/lib/sprockets/legacy.rb +0 -314
- data/lib/sprockets/legacy_proc_processor.rb +0 -35
- data/lib/sprockets/legacy_tilt_processor.rb +0 -29
- data/lib/sprockets/sass_template.rb +0 -7
data/lib/sprockets/manifest.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'json'
|
2
3
|
require 'time'
|
4
|
+
|
5
|
+
require 'concurrent'
|
6
|
+
|
3
7
|
require 'sprockets/manifest_utils'
|
4
8
|
|
5
9
|
module Sprockets
|
@@ -11,7 +15,7 @@ module Sprockets
|
|
11
15
|
# The JSON is part of the public API and should be considered stable. This
|
12
16
|
# should make it easy to read from other programming languages and processes
|
13
17
|
# that don't have sprockets loaded. See `#assets` and `#files` for more
|
14
|
-
#
|
18
|
+
# information about the structure.
|
15
19
|
class Manifest
|
16
20
|
include ManifestUtils
|
17
21
|
|
@@ -48,14 +52,8 @@ module Sprockets
|
|
48
52
|
@directory ||= File.dirname(@filename) if @filename
|
49
53
|
|
50
54
|
# If directory is given w/o filename, pick a random manifest location
|
51
|
-
@rename_filename = nil
|
52
55
|
if @directory && @filename.nil?
|
53
|
-
@filename = find_directory_manifest(@directory)
|
54
|
-
|
55
|
-
# If legacy manifest name autodetected, mark to rename on save
|
56
|
-
if File.basename(@filename).start_with?("manifest")
|
57
|
-
@rename_filename = File.join(@directory, generate_manifest_path)
|
58
|
-
end
|
56
|
+
@filename = find_directory_manifest(@directory, logger)
|
59
57
|
end
|
60
58
|
|
61
59
|
unless @directory && @filename
|
@@ -114,38 +112,46 @@ module Sprockets
|
|
114
112
|
# Public: Find all assets matching pattern set in environment.
|
115
113
|
#
|
116
114
|
# Returns Enumerator of Assets.
|
117
|
-
def find(*args)
|
115
|
+
def find(*args, &block)
|
118
116
|
unless environment
|
119
117
|
raise Error, "manifest requires environment for compilation"
|
120
118
|
end
|
121
119
|
|
122
120
|
return to_enum(__method__, *args) unless block_given?
|
123
121
|
|
124
|
-
paths, filters = args.flatten.partition { |arg| self.class.simple_logical_path?(arg) }
|
125
|
-
filters = filters.map { |arg| self.class.compile_match_filter(arg) }
|
126
|
-
|
127
122
|
environment = self.environment.cached
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
yield asset
|
123
|
+
promises = args.flatten.map do |path|
|
124
|
+
Concurrent::Promise.execute(executor: executor) do
|
125
|
+
environment.find_all_linked_assets(path).to_a
|
132
126
|
end
|
133
127
|
end
|
134
128
|
|
135
|
-
|
136
|
-
|
137
|
-
if filters.any? { |f| f.call(logical_path, filename) }
|
138
|
-
environment.find_all_linked_assets(filename) do |asset|
|
139
|
-
yield asset
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
129
|
+
promises.each do |promise|
|
130
|
+
promise.value!.each(&block)
|
143
131
|
end
|
144
132
|
|
145
133
|
nil
|
146
134
|
end
|
147
135
|
|
148
|
-
#
|
136
|
+
# Public: Find the source of assets by paths.
|
137
|
+
#
|
138
|
+
# Returns Enumerator of assets file content.
|
139
|
+
def find_sources(*args)
|
140
|
+
return to_enum(__method__, *args) unless block_given?
|
141
|
+
|
142
|
+
if environment
|
143
|
+
find(*args).each do |asset|
|
144
|
+
yield asset.source
|
145
|
+
end
|
146
|
+
else
|
147
|
+
args.each do |path|
|
148
|
+
asset = assets[path]
|
149
|
+
yield File.binread(File.join(dir, asset)) if asset
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Compile asset to directory. The asset is written to a
|
149
155
|
# fingerprinted filename like
|
150
156
|
# `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
|
151
157
|
# also inserted into the manifest file.
|
@@ -157,29 +163,46 @@ module Sprockets
|
|
157
163
|
raise Error, "manifest requires environment for compilation"
|
158
164
|
end
|
159
165
|
|
160
|
-
filenames
|
166
|
+
filenames = []
|
167
|
+
concurrent_exporters = []
|
161
168
|
|
169
|
+
assets_to_export = Concurrent::Array.new
|
162
170
|
find(*args) do |asset|
|
171
|
+
assets_to_export << asset
|
172
|
+
end
|
173
|
+
|
174
|
+
assets_to_export.each do |asset|
|
175
|
+
mtime = Time.now.iso8601
|
163
176
|
files[asset.digest_path] = {
|
164
177
|
'logical_path' => asset.logical_path,
|
165
|
-
'mtime' =>
|
178
|
+
'mtime' => mtime,
|
166
179
|
'size' => asset.bytesize,
|
167
180
|
'digest' => asset.hexdigest,
|
168
|
-
|
181
|
+
|
182
|
+
# Deprecated: Remove beta integrity attribute in next release.
|
183
|
+
# Callers should DigestUtils.hexdigest_integrity_uri to compute the
|
184
|
+
# digest themselves.
|
185
|
+
'integrity' => DigestUtils.hexdigest_integrity_uri(asset.hexdigest)
|
169
186
|
}
|
170
187
|
assets[asset.logical_path] = asset.digest_path
|
171
188
|
|
172
|
-
|
189
|
+
filenames << asset.filename
|
173
190
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
logger.info "Writing #{target}"
|
178
|
-
asset.write_to target
|
179
|
-
end
|
191
|
+
promise = nil
|
192
|
+
exporters_for_asset(asset) do |exporter|
|
193
|
+
next if exporter.skip?(logger)
|
180
194
|
|
181
|
-
|
195
|
+
if promise.nil?
|
196
|
+
promise = Concurrent::Promise.new(executor: executor) { exporter.call }
|
197
|
+
concurrent_exporters << promise.execute
|
198
|
+
else
|
199
|
+
concurrent_exporters << promise.then { exporter.call }
|
200
|
+
end
|
201
|
+
end
|
182
202
|
end
|
203
|
+
|
204
|
+
# make sure all exporters have finished before returning the main thread
|
205
|
+
concurrent_exporters.each(&:wait!)
|
183
206
|
save
|
184
207
|
|
185
208
|
filenames
|
@@ -192,6 +215,7 @@ module Sprockets
|
|
192
215
|
#
|
193
216
|
def remove(filename)
|
194
217
|
path = File.join(dir, filename)
|
218
|
+
gzip = "#{path}.gz"
|
195
219
|
logical_path = files[filename]['logical_path']
|
196
220
|
|
197
221
|
if assets[logical_path] == filename
|
@@ -200,6 +224,7 @@ module Sprockets
|
|
200
224
|
|
201
225
|
files.delete(filename)
|
202
226
|
FileUtils.rm(path) if File.exist?(path)
|
227
|
+
FileUtils.rm(gzip) if File.exist?(gzip)
|
203
228
|
|
204
229
|
save
|
205
230
|
|
@@ -230,9 +255,9 @@ module Sprockets
|
|
230
255
|
# Sort by timestamp
|
231
256
|
Time.parse(attrs['mtime'])
|
232
257
|
}.reverse.each_with_index.drop_while { |(_, attrs), index|
|
233
|
-
|
258
|
+
_age = [0, Time.now - Time.parse(attrs['mtime'])].max
|
234
259
|
# Keep if under age or within the count limit
|
235
|
-
|
260
|
+
_age < age || index < count
|
236
261
|
}.each { |(path, _), _|
|
237
262
|
# Remove old assets
|
238
263
|
remove(path)
|
@@ -244,18 +269,13 @@ module Sprockets
|
|
244
269
|
def clobber
|
245
270
|
FileUtils.rm_r(directory) if File.exist?(directory)
|
246
271
|
logger.info "Removed #{directory}"
|
272
|
+
# if we have an environment clear the cache too
|
273
|
+
environment.cache.clear if environment
|
247
274
|
nil
|
248
275
|
end
|
249
276
|
|
250
|
-
# Persist
|
277
|
+
# Persist manifest back to FS
|
251
278
|
def save
|
252
|
-
if @rename_filename
|
253
|
-
logger.info "Renaming #{@filename} to #{@rename_filename}"
|
254
|
-
FileUtils.mv(@filename, @rename_filename)
|
255
|
-
@filename = @rename_filename
|
256
|
-
@rename_filename = nil
|
257
|
-
end
|
258
|
-
|
259
279
|
data = json_encode(@data)
|
260
280
|
FileUtils.mkdir_p File.dirname(@filename)
|
261
281
|
PathUtils.atomic_write(@filename) do |f|
|
@@ -264,6 +284,36 @@ module Sprockets
|
|
264
284
|
end
|
265
285
|
|
266
286
|
private
|
287
|
+
|
288
|
+
# Given an asset, finds all exporters that
|
289
|
+
# match its mime-type.
|
290
|
+
#
|
291
|
+
# Will yield each expoter to the passed in block.
|
292
|
+
#
|
293
|
+
# array = []
|
294
|
+
# puts asset.content_type # => "application/javascript"
|
295
|
+
# exporters_for_asset(asset) do |exporter|
|
296
|
+
# array << exporter
|
297
|
+
# end
|
298
|
+
# # puts array => [Exporters::FileExporter, Exporters::ZlibExporter]
|
299
|
+
def exporters_for_asset(asset)
|
300
|
+
exporters = [Exporters::FileExporter]
|
301
|
+
|
302
|
+
environment.exporters.each do |mime_type, exporter_list|
|
303
|
+
next unless asset.content_type
|
304
|
+
next unless environment.match_mime_type? asset.content_type, mime_type
|
305
|
+
exporter_list.each do |exporter|
|
306
|
+
exporters << exporter
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
exporters.uniq!
|
311
|
+
|
312
|
+
exporters.each do |exporter|
|
313
|
+
yield exporter.new(asset: asset, environment: environment, directory: dir)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
267
317
|
def json_decode(obj)
|
268
318
|
JSON.parse(obj, create_additions: false)
|
269
319
|
end
|
@@ -281,5 +331,9 @@ module Sprockets
|
|
281
331
|
logger
|
282
332
|
end
|
283
333
|
end
|
334
|
+
|
335
|
+
def executor
|
336
|
+
@executor ||= environment.export_concurrent ? :fast : :immediate
|
337
|
+
end
|
284
338
|
end
|
285
339
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'securerandom'
|
3
|
+
require 'logger'
|
2
4
|
|
3
5
|
module Sprockets
|
4
6
|
# Public: Manifest utilities.
|
@@ -6,7 +8,6 @@ module Sprockets
|
|
6
8
|
extend self
|
7
9
|
|
8
10
|
MANIFEST_RE = /^\.sprockets-manifest-[0-9a-f]{32}.json$/
|
9
|
-
LEGACY_MANIFEST_RE = /^manifest(-[0-9a-f]{32})?.json$/
|
10
11
|
|
11
12
|
# Public: Generate a new random manifest path.
|
12
13
|
#
|
@@ -33,12 +34,14 @@ module Sprockets
|
|
33
34
|
# # => "/app/public/assets/.sprockets-manifest-abc123.json"
|
34
35
|
#
|
35
36
|
# Returns String filename.
|
36
|
-
def find_directory_manifest(dirname)
|
37
|
+
def find_directory_manifest(dirname, logger = Logger.new($stderr))
|
37
38
|
entries = File.directory?(dirname) ? Dir.entries(dirname) : []
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
manifest_entries = entries.select { |e| e =~ MANIFEST_RE }
|
40
|
+
if manifest_entries.length > 1
|
41
|
+
manifest_entries.sort!
|
42
|
+
logger.warn("Found multiple manifests: #{manifest_entries}. Choosing the first alphabetically: #{manifest_entries.first}")
|
43
|
+
end
|
44
|
+
entry = manifest_entries.first || generate_manifest_path
|
42
45
|
File.join(dirname, entry)
|
43
46
|
end
|
44
47
|
end
|
data/lib/sprockets/mime.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/encoding_utils'
|
2
3
|
require 'sprockets/http_utils'
|
3
4
|
require 'sprockets/utils'
|
@@ -36,29 +37,18 @@ module Sprockets
|
|
36
37
|
|
37
38
|
# Public: Register a new mime type.
|
38
39
|
#
|
39
|
-
# mime_type
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# See EncodingUtils.
|
40
|
+
# mime_type - String MIME Type
|
41
|
+
# extensions - Array of String extnames
|
42
|
+
# charset - Proc/Method that detects the charset of a file.
|
43
|
+
# See EncodingUtils.
|
44
44
|
#
|
45
45
|
# Returns nothing.
|
46
|
-
def register_mime_type(mime_type,
|
47
|
-
|
48
|
-
if options.is_a?(String)
|
49
|
-
options = { extensions: [options] }
|
50
|
-
end
|
51
|
-
|
52
|
-
extnames = Array(options[:extensions]).map { |extname|
|
53
|
-
Sprockets::Utils.normalize_extension(extname)
|
54
|
-
}
|
46
|
+
def register_mime_type(mime_type, extensions: [], charset: nil)
|
47
|
+
extnames = Array(extensions)
|
55
48
|
|
56
|
-
charset = options[:charset]
|
57
49
|
charset ||= :default if mime_type.start_with?('text/')
|
58
50
|
charset = EncodingUtils::CHARSET_DETECT[charset] if charset.is_a?(Symbol)
|
59
51
|
|
60
|
-
self.computed_config = {}
|
61
|
-
|
62
52
|
self.config = hash_reassoc(config, :mime_exts) do |mime_exts|
|
63
53
|
extnames.each do |extname|
|
64
54
|
mime_exts[extname] = mime_type
|
@@ -97,34 +87,10 @@ module Sprockets
|
|
97
87
|
data = File.binread(filename)
|
98
88
|
|
99
89
|
if detect = mime_type_charset_detecter(content_type)
|
100
|
-
detect.call(data).encode(Encoding::UTF_8, :
|
90
|
+
detect.call(data).encode(Encoding::UTF_8, universal_newline: true)
|
101
91
|
else
|
102
92
|
data
|
103
93
|
end
|
104
94
|
end
|
105
|
-
|
106
|
-
private
|
107
|
-
def extname_map
|
108
|
-
self.computed_config[:_extnames] ||= compute_extname_map
|
109
|
-
end
|
110
|
-
|
111
|
-
def compute_extname_map
|
112
|
-
graph = {}
|
113
|
-
|
114
|
-
([nil] + pipelines.keys.map(&:to_s)).each do |pipeline|
|
115
|
-
pipeline_extname = ".#{pipeline}" if pipeline
|
116
|
-
([[nil, nil]] + config[:mime_exts].to_a).each do |format_extname, format_type|
|
117
|
-
4.times do |n|
|
118
|
-
config[:engines].keys.permutation(n).each do |engine_extnames|
|
119
|
-
key = "#{pipeline_extname}#{format_extname}#{engine_extnames.join}"
|
120
|
-
type = format_type || config[:engine_mime_types][engine_extnames.first]
|
121
|
-
graph[key] = {type: type, engines: engine_extnames, pipeline: pipeline}
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
graph
|
128
|
-
end
|
129
95
|
end
|
130
96
|
end
|
@@ -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 candidate 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
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'set'
|
2
3
|
require 'sprockets/path_utils'
|
3
4
|
require 'sprockets/uri_utils'
|
@@ -41,7 +42,7 @@ module Sprockets
|
|
41
42
|
#
|
42
43
|
# Returns an Array of entry names and a Set of dependency URIs.
|
43
44
|
def entries_with_dependencies(path)
|
44
|
-
return entries(path),
|
45
|
+
return entries(path), Set.new([build_file_digest_uri(path)])
|
45
46
|
end
|
46
47
|
|
47
48
|
# Internal: List directory filenames and associated Stats under a
|
@@ -53,16 +54,7 @@ module Sprockets
|
|
53
54
|
#
|
54
55
|
# Returns an Array of filenames and a Set of dependency URIs.
|
55
56
|
def stat_directory_with_dependencies(dir)
|
56
|
-
return stat_directory(dir).to_a,
|
57
|
-
end
|
58
|
-
|
59
|
-
# Internal: Returns a set of dependencies for a particular path.
|
60
|
-
#
|
61
|
-
# path - String directory path
|
62
|
-
#
|
63
|
-
# Returns a Set of dependency URIs.
|
64
|
-
def file_digest_dependency_set(path)
|
65
|
-
Set.new([build_file_digest_uri(path)])
|
57
|
+
return stat_directory(dir).to_a, Set.new([build_file_digest_uri(dir)])
|
66
58
|
end
|
67
59
|
|
68
60
|
# Internal: List directory filenames and associated Stats under an entire
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/digest_utils'
|
2
3
|
require 'sprockets/path_utils'
|
3
4
|
|
@@ -15,7 +16,7 @@ module Sprockets
|
|
15
16
|
def stat_digest(path, stat)
|
16
17
|
if stat.directory?
|
17
18
|
# If its a directive, digest the list of filenames
|
18
|
-
digest_class.digest(self.entries(path).join(','))
|
19
|
+
digest_class.digest(self.entries(path).join(','.freeze))
|
19
20
|
elsif stat.file?
|
20
21
|
# If its a file, digest the contents
|
21
22
|
digest_class.file(path.to_s).digest
|
data/lib/sprockets/path_utils.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
module Sprockets
|
4
3
|
# Internal: File and path related utilities. Mixed into Environment.
|
5
4
|
#
|
@@ -7,6 +6,7 @@ module Sprockets
|
|
7
6
|
# when code actually wants to reference ::FileUtils.
|
8
7
|
module PathUtils
|
9
8
|
extend self
|
9
|
+
require 'pathname'
|
10
10
|
|
11
11
|
# Public: Like `File.stat`.
|
12
12
|
#
|
@@ -55,9 +55,14 @@ module Sprockets
|
|
55
55
|
# Returns an empty `Array` if the directory does not exist.
|
56
56
|
def entries(path)
|
57
57
|
if File.directory?(path)
|
58
|
-
Dir.entries(path, :
|
59
|
-
|
60
|
-
|
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
|
61
66
|
else
|
62
67
|
[]
|
63
68
|
end
|
@@ -69,8 +74,6 @@ module Sprockets
|
|
69
74
|
#
|
70
75
|
# Returns true if path is absolute, otherwise false.
|
71
76
|
if File::ALT_SEPARATOR
|
72
|
-
require 'pathname'
|
73
|
-
|
74
77
|
# On Windows, ALT_SEPARATOR is \
|
75
78
|
# Delegate to Pathname since the logic gets complex.
|
76
79
|
def absolute_path?(path)
|
@@ -78,7 +81,7 @@ module Sprockets
|
|
78
81
|
end
|
79
82
|
else
|
80
83
|
def absolute_path?(path)
|
81
|
-
path
|
84
|
+
path.start_with?(File::SEPARATOR)
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
@@ -95,7 +98,58 @@ module Sprockets
|
|
95
98
|
#
|
96
99
|
# Returns true if path is relative, otherwise false.
|
97
100
|
def relative_path?(path)
|
98
|
-
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}"
|
99
153
|
end
|
100
154
|
|
101
155
|
# Internal: Get relative path for root path and subpath.
|
@@ -107,8 +161,8 @@ module Sprockets
|
|
107
161
|
# subpath is outside of path.
|
108
162
|
def split_subpath(path, subpath)
|
109
163
|
return "" if path == subpath
|
110
|
-
path = File.join(path, '')
|
111
|
-
if subpath
|
164
|
+
path = File.join(path, ''.freeze)
|
165
|
+
if subpath&.start_with?(path)
|
112
166
|
subpath[path.length..-1]
|
113
167
|
else
|
114
168
|
nil
|
@@ -146,16 +200,47 @@ module Sprockets
|
|
146
200
|
#
|
147
201
|
# Returns [String extname, Object value] or nil nothing matched.
|
148
202
|
def match_path_extname(path, extensions)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
156
241
|
end
|
157
242
|
end
|
158
|
-
|
243
|
+
matches
|
159
244
|
end
|
160
245
|
|
161
246
|
# Internal: Returns all parents for path
|
@@ -267,16 +352,16 @@ module Sprockets
|
|
267
352
|
Thread.current.object_id,
|
268
353
|
Process.pid,
|
269
354
|
rand(1000000)
|
270
|
-
].join('.')
|
355
|
+
].join('.'.freeze)
|
271
356
|
tmpname = File.join(dirname, basename)
|
272
357
|
|
273
358
|
File.open(tmpname, 'wb+') do |f|
|
274
359
|
yield f
|
275
360
|
end
|
276
361
|
|
277
|
-
|
362
|
+
File.rename(tmpname, filename)
|
278
363
|
ensure
|
279
|
-
|
364
|
+
File.delete(tmpname) if File.exist?(tmpname)
|
280
365
|
end
|
281
366
|
end
|
282
367
|
end
|
data/lib/sprockets/paths.rb
CHANGED