sprockets 3.0.0 → 4.0.0
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +68 -0
- data/README.md +397 -408
- data/bin/sprockets +12 -7
- data/lib/rake/sprocketstask.rb +3 -2
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +19 -23
- 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 +59 -11
- data/lib/sprockets/bower.rb +5 -2
- data/lib/sprockets/bundle.rb +44 -4
- data/lib/sprockets/cache/file_store.rb +32 -7
- data/lib/sprockets/cache/memory_store.rb +9 -0
- data/lib/sprockets/cache/null_store.rb +8 -0
- data/lib/sprockets/cache.rb +42 -5
- data/lib/sprockets/cached_environment.rb +14 -19
- data/lib/sprockets/closure_compressor.rb +6 -11
- data/lib/sprockets/coffee_script_processor.rb +19 -5
- data/lib/sprockets/compressing.rb +62 -2
- data/lib/sprockets/configuration.rb +3 -7
- data/lib/sprockets/context.rb +98 -23
- data/lib/sprockets/dependencies.rb +9 -8
- data/lib/sprockets/digest_utils.rb +104 -60
- data/lib/sprockets/directive_processor.rb +45 -35
- data/lib/sprockets/eco_processor.rb +3 -2
- data/lib/sprockets/ejs_processor.rb +3 -2
- data/lib/sprockets/encoding_utils.rb +8 -4
- data/lib/sprockets/environment.rb +9 -4
- data/lib/sprockets/erb_processor.rb +28 -21
- data/lib/sprockets/errors.rb +1 -1
- data/lib/sprockets/exporters/base.rb +72 -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 +26 -6
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +11 -10
- data/lib/sprockets/loader.rb +236 -69
- data/lib/sprockets/manifest.rb +97 -44
- 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 +106 -21
- data/lib/sprockets/paths.rb +1 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +31 -51
- data/lib/sprockets/processor_utils.rb +81 -15
- data/lib/sprockets/resolve.rb +182 -95
- 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 +45 -17
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +57 -34
- 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 +15 -14
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +43 -59
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +5 -14
- data/lib/sprockets.rb +103 -33
- metadata +151 -22
- data/LICENSE +0 -21
- 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
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'sprockets/uri_utils'
|
3
|
+
require 'sprockets/uri_tar'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
# Internal: Used to parse and store the URI to an unloaded asset
|
7
|
+
# Generates keys used to store and retrieve items from cache
|
8
|
+
class UnloadedAsset
|
9
|
+
|
10
|
+
# Internal: Initialize object for generating cache keys
|
11
|
+
#
|
12
|
+
# uri - A String containing complete URI to a file including scheme
|
13
|
+
# and full path such as
|
14
|
+
# "file:///Path/app/assets/js/app.js?type=application/javascript"
|
15
|
+
# env - The current "environment" that assets are being loaded into.
|
16
|
+
# We need it so we know where the +root+ (directory where Sprockets
|
17
|
+
# is being invoked). We also need it for the `file_digest` method,
|
18
|
+
# since, for some strange reason, memoization is provided by
|
19
|
+
# overriding methods such as `stat` in the `PathUtils` module.
|
20
|
+
#
|
21
|
+
# Returns UnloadedAsset.
|
22
|
+
def initialize(uri, env)
|
23
|
+
@uri = uri.to_s
|
24
|
+
@env = env
|
25
|
+
@compressed_path = URITar.new(uri, env).compressed_path
|
26
|
+
@params = nil # lazy loaded
|
27
|
+
@filename = nil # lazy loaded
|
28
|
+
end
|
29
|
+
attr_reader :compressed_path, :uri
|
30
|
+
|
31
|
+
# Internal: Full file path without schema
|
32
|
+
#
|
33
|
+
# This returns a string containing the full path to the asset without the schema.
|
34
|
+
# Information is loaded lazily since we want `UnloadedAsset.new(dep, self).relative_path`
|
35
|
+
# to be fast. Calling this method the first time allocates an array and a hash.
|
36
|
+
#
|
37
|
+
# Example
|
38
|
+
#
|
39
|
+
# If the URI is `file:///Full/path/app/assets/javascripts/application.js"` then the
|
40
|
+
# filename would be `"/Full/path/app/assets/javascripts/application.js"`
|
41
|
+
#
|
42
|
+
# Returns a String.
|
43
|
+
def filename
|
44
|
+
unless @filename
|
45
|
+
load_file_params
|
46
|
+
end
|
47
|
+
@filename
|
48
|
+
end
|
49
|
+
|
50
|
+
# Internal: Hash of param values
|
51
|
+
#
|
52
|
+
# This information is generated and used internally by Sprockets.
|
53
|
+
# Known keys include `:type` which stores the asset's mime-type, `:id` which is a fully resolved
|
54
|
+
# digest for the asset (includes dependency digest as opposed to a digest of only file contents)
|
55
|
+
# and `:pipeline`. Hash may be empty.
|
56
|
+
#
|
57
|
+
# Example
|
58
|
+
#
|
59
|
+
# If the URI is `file:///Full/path/app/assets/javascripts/application.js"type=application/javascript`
|
60
|
+
# Then the params would be `{type: "application/javascript"}`
|
61
|
+
#
|
62
|
+
# Returns a Hash.
|
63
|
+
def params
|
64
|
+
unless @params
|
65
|
+
load_file_params
|
66
|
+
end
|
67
|
+
@params
|
68
|
+
end
|
69
|
+
|
70
|
+
# Internal: Key of asset
|
71
|
+
#
|
72
|
+
# Used to retrieve an asset from the cache based on "compressed" path to asset.
|
73
|
+
# A "compressed" path can either be relative to the root of the project or an
|
74
|
+
# absolute path.
|
75
|
+
#
|
76
|
+
# Returns a String.
|
77
|
+
def asset_key
|
78
|
+
"asset-uri:#{compressed_path}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Dependency History key
|
82
|
+
#
|
83
|
+
# Used to retrieve an array of "histories" each of which contains a set of stored dependencies
|
84
|
+
# for a given asset path and filename digest.
|
85
|
+
#
|
86
|
+
# A dependency can refer to either an asset e.g. index.js
|
87
|
+
# may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
|
88
|
+
# compilation, such as the VERSION of Sprockets (i.e. the environment) and what "processors"
|
89
|
+
# are used.
|
90
|
+
#
|
91
|
+
# For example a history array with one Set of dependencies may look like:
|
92
|
+
#
|
93
|
+
# [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css",
|
94
|
+
# "file-digest:///Full/path/app/assets/stylesheets/application.css",
|
95
|
+
# "processors:type=text/css&file_type=text/css&pipeline=self",
|
96
|
+
# "file-digest:///Full/path/app/assets/stylesheets"]]
|
97
|
+
#
|
98
|
+
# This method of asset lookup is used to ensure that none of the dependencies have been modified
|
99
|
+
# since last lookup. If one of them has, the key will be different and a new entry must be stored.
|
100
|
+
#
|
101
|
+
# URI dependencies are later converted to "compressed" paths
|
102
|
+
#
|
103
|
+
# Returns a String.
|
104
|
+
def dependency_history_key
|
105
|
+
"asset-uri-cache-dependencies:#{compressed_path}:#{ @env.file_digest(filename) }"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Internal: Digest key
|
109
|
+
#
|
110
|
+
# Used to retrieve a string containing the "compressed" path to an asset based on
|
111
|
+
# a digest. The digest is generated from dependencies stored via information stored in
|
112
|
+
# the `dependency_history_key` after each of the "dependencies" is "resolved".
|
113
|
+
# For example "environment-version" may be resolved to "environment-1.0-3.2.0"
|
114
|
+
# for version "3.2.0" of Sprockets
|
115
|
+
#
|
116
|
+
# Returns a String.
|
117
|
+
def digest_key(digest)
|
118
|
+
"asset-uri-digest:#{compressed_path}:#{digest}"
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: File digest key
|
122
|
+
#
|
123
|
+
# The digest for a given file won't change if the path and the stat time hasn't changed
|
124
|
+
# We can save time by not re-computing this information and storing it in the cache
|
125
|
+
#
|
126
|
+
# Returns a String.
|
127
|
+
def file_digest_key(stat)
|
128
|
+
"file_digest:#{compressed_path}:#{stat}"
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
# Internal: Parses uri into filename and params hash
|
133
|
+
#
|
134
|
+
# Returns Array with filename and params hash
|
135
|
+
def load_file_params
|
136
|
+
@filename, @params = URIUtils.parse_asset_uri(uri)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'sprockets/path_utils'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
# Internal: used to "expand" and "compress" values for storage
|
6
|
+
class URITar
|
7
|
+
attr_reader :scheme, :root, :path
|
8
|
+
|
9
|
+
# Internal: Initialize object for compression or expansion
|
10
|
+
#
|
11
|
+
# uri - A String containing URI that may or may not contain the scheme
|
12
|
+
# env - The current "environment" that assets are being loaded into.
|
13
|
+
def initialize(uri, env)
|
14
|
+
@root = env.root
|
15
|
+
@env = env
|
16
|
+
uri = uri.to_s
|
17
|
+
if uri.include?("://".freeze)
|
18
|
+
@scheme, _, @path = uri.partition("://".freeze)
|
19
|
+
@scheme << "://".freeze
|
20
|
+
else
|
21
|
+
@scheme = "".freeze
|
22
|
+
@path = uri
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Internal: Converts full uri to a "compressed" uri
|
27
|
+
#
|
28
|
+
# If a uri is inside of an environment's root it will
|
29
|
+
# be shortened to be a relative path.
|
30
|
+
#
|
31
|
+
# If a uri is outside of the environment's root the original
|
32
|
+
# uri will be returned.
|
33
|
+
#
|
34
|
+
# Returns String
|
35
|
+
def compress
|
36
|
+
scheme + compressed_path
|
37
|
+
end
|
38
|
+
|
39
|
+
# Internal: Tells us if we are using an absolute path
|
40
|
+
#
|
41
|
+
# Nix* systems start with a `/` like /Users/schneems.
|
42
|
+
# Windows systems start with a drive letter than colon and slash
|
43
|
+
# like C:/Schneems.
|
44
|
+
def absolute_path?
|
45
|
+
PathUtils.absolute_path?(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Internal: Convert a "compressed" uri to an absolute path
|
49
|
+
#
|
50
|
+
# If a uri is inside of the environment's root it will not
|
51
|
+
# start with a slash for example:
|
52
|
+
#
|
53
|
+
# file://this/is/a/relative/path
|
54
|
+
#
|
55
|
+
# If a uri is outside the root, it will start with a slash:
|
56
|
+
#
|
57
|
+
# file:///This/is/an/absolute/path
|
58
|
+
#
|
59
|
+
# Returns String
|
60
|
+
def expand
|
61
|
+
if absolute_path?
|
62
|
+
# Stored path was absolute, don't add root
|
63
|
+
scheme + path
|
64
|
+
else
|
65
|
+
if scheme.empty?
|
66
|
+
File.join(root, path)
|
67
|
+
else
|
68
|
+
# We always want to return an absolute uri,
|
69
|
+
# make sure the path starts with a slash.
|
70
|
+
scheme + File.join("/".freeze, root, path)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Internal: Returns "compressed" path
|
76
|
+
#
|
77
|
+
# If the input uri is relative to the environment root
|
78
|
+
# it will return a path relative to the environment root.
|
79
|
+
# Otherwise an absolute path will be returned.
|
80
|
+
#
|
81
|
+
# Only path information is returned, and not scheme.
|
82
|
+
#
|
83
|
+
# Returns String
|
84
|
+
def compressed_path
|
85
|
+
# windows
|
86
|
+
if !@root.start_with?("/".freeze) && path.start_with?("/".freeze)
|
87
|
+
consistent_root = "/".freeze + @root
|
88
|
+
else
|
89
|
+
consistent_root = @root
|
90
|
+
end
|
91
|
+
|
92
|
+
if compressed_path = PathUtils.split_subpath(consistent_root, path)
|
93
|
+
compressed_path
|
94
|
+
else
|
95
|
+
path
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/sprockets/uri_utils.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'uri'
|
2
3
|
|
3
4
|
module Sprockets
|
@@ -17,9 +18,6 @@ module Sprockets
|
|
17
18
|
#
|
18
19
|
# pipeline - String name of pipeline.
|
19
20
|
#
|
20
|
-
# encoding - A content encoding such as "gzip" or "deflate". NOT a charset
|
21
|
-
# like "utf-8".
|
22
|
-
#
|
23
21
|
module URIUtils
|
24
22
|
extend self
|
25
23
|
|
@@ -50,8 +48,10 @@ module Sprockets
|
|
50
48
|
path = URI::Generic::DEFAULT_PARSER.unescape(path)
|
51
49
|
path.force_encoding(Encoding::UTF_8)
|
52
50
|
|
53
|
-
# Hack for parsing Windows "
|
54
|
-
|
51
|
+
# Hack for parsing Windows "/C:/Users/IEUser" paths
|
52
|
+
if File::ALT_SEPARATOR && path[2] == ':'
|
53
|
+
path = path[1..-1]
|
54
|
+
end
|
55
55
|
|
56
56
|
[scheme, host, path, query]
|
57
57
|
end
|
@@ -60,9 +60,9 @@ module Sprockets
|
|
60
60
|
#
|
61
61
|
# Returns String.
|
62
62
|
def join_file_uri(scheme, host, path, query)
|
63
|
-
str = "#{scheme}://"
|
63
|
+
str = +"#{scheme}://"
|
64
64
|
str << host if host
|
65
|
-
path = "/#{path}" unless path.start_with?("/")
|
65
|
+
path = "/#{path}" unless path.start_with?("/".freeze)
|
66
66
|
str << URI::Generic::DEFAULT_PARSER.escape(path)
|
67
67
|
str << "?#{query}" if query
|
68
68
|
str
|
@@ -75,7 +75,7 @@ module Sprockets
|
|
75
75
|
# Returns true or false.
|
76
76
|
def valid_asset_uri?(str)
|
77
77
|
# Quick prefix check before attempting a full parse
|
78
|
-
str.start_with?("file://") && parse_asset_uri(str) ? true : false
|
78
|
+
str.start_with?("file://".freeze) && parse_asset_uri(str) ? true : false
|
79
79
|
rescue URI::InvalidURIError
|
80
80
|
false
|
81
81
|
end
|
@@ -128,7 +128,7 @@ module Sprockets
|
|
128
128
|
def parse_file_digest_uri(uri)
|
129
129
|
scheme, _, path, _ = split_file_uri(uri)
|
130
130
|
|
131
|
-
unless scheme == 'file-digest'
|
131
|
+
unless scheme == 'file-digest'.freeze
|
132
132
|
raise URI::InvalidURIError, "expected file-digest scheme: #{uri}"
|
133
133
|
end
|
134
134
|
|
@@ -146,7 +146,7 @@ module Sprockets
|
|
146
146
|
#
|
147
147
|
# Returns String URI.
|
148
148
|
def build_file_digest_uri(path)
|
149
|
-
join_file_uri(
|
149
|
+
join_file_uri('file-digest'.freeze, nil, path, nil)
|
150
150
|
end
|
151
151
|
|
152
152
|
# Internal: Serialize hash of params into query string.
|
@@ -171,7 +171,7 @@ module Sprockets
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
174
|
-
"#{query.join('&')}" if query.any?
|
174
|
+
"#{query.join('&'.freeze)}" if query.any?
|
175
175
|
end
|
176
176
|
|
177
177
|
# Internal: Parse query string into hash of params
|
@@ -180,10 +180,11 @@ module Sprockets
|
|
180
180
|
#
|
181
181
|
# Return Hash of params.
|
182
182
|
def parse_uri_query_params(query)
|
183
|
-
query.to_s.split('&').reduce({}) do |h, p|
|
184
|
-
k, v = p.split('=', 2)
|
183
|
+
query.to_s.split('&'.freeze).reduce({}) do |h, p|
|
184
|
+
k, v = p.split('='.freeze, 2)
|
185
185
|
v = URI::Generic::DEFAULT_PARSER.unescape(v) if v
|
186
|
-
h
|
186
|
+
h[k.to_sym] = v || true
|
187
|
+
h
|
187
188
|
end
|
188
189
|
end
|
189
190
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Sprockets
|
3
|
+
module Utils
|
4
|
+
class Gzip
|
5
|
+
# Private: Generates a gzipped file based off of reference asset.
|
6
|
+
#
|
7
|
+
# ZlibArchiver.call(file, source, mtime)
|
8
|
+
#
|
9
|
+
# Compresses a given `source` using stdlib Zlib algorithm
|
10
|
+
# writes contents to the `file` passed in. Sets `mtime` of
|
11
|
+
# written file to passed in `mtime`
|
12
|
+
module ZlibArchiver
|
13
|
+
def self.call(file, source, mtime)
|
14
|
+
gz = Zlib::GzipWriter.new(file, Zlib::BEST_COMPRESSION)
|
15
|
+
gz.mtime = mtime
|
16
|
+
gz.write(source)
|
17
|
+
gz.close
|
18
|
+
|
19
|
+
File.utime(mtime, mtime, file.path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Private: Generates a gzipped file based off of reference asset.
|
24
|
+
#
|
25
|
+
# ZopfliArchiver.call(file, source, mtime)
|
26
|
+
#
|
27
|
+
# Compresses a given `source` using the zopfli gem
|
28
|
+
# writes contents to the `file` passed in. Sets `mtime` of
|
29
|
+
# written file to passed in `mtime`
|
30
|
+
module ZopfliArchiver
|
31
|
+
def self.call(file, source, mtime)
|
32
|
+
compressed_source = Autoload::Zopfli.deflate(source, format: :gzip, mtime: mtime)
|
33
|
+
file.write(compressed_source)
|
34
|
+
file.close
|
35
|
+
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :content_type, :source, :charset, :archiver
|
41
|
+
|
42
|
+
# Private: Generates a gzipped file based off of reference file.
|
43
|
+
def initialize(asset, archiver: ZlibArchiver)
|
44
|
+
@content_type = asset.content_type
|
45
|
+
@source = asset.source
|
46
|
+
@charset = asset.charset
|
47
|
+
@archiver = archiver
|
48
|
+
end
|
49
|
+
|
50
|
+
# What non-text mime types should we compress? This list comes from:
|
51
|
+
# https://www.fastly.com/blog/new-gzip-settings-and-deciding-what-compress
|
52
|
+
COMPRESSABLE_MIME_TYPES = {
|
53
|
+
"application/vnd.ms-fontobject" => true,
|
54
|
+
"application/x-font-opentype" => true,
|
55
|
+
"application/x-font-ttf" => true,
|
56
|
+
"image/x-icon" => true,
|
57
|
+
"image/svg+xml" => true
|
58
|
+
}
|
59
|
+
|
60
|
+
# Private: Returns whether or not an asset can be compressed.
|
61
|
+
#
|
62
|
+
# We want to compress any file that is text based.
|
63
|
+
# You do not want to compress binary
|
64
|
+
# files as they may already be compressed and running them
|
65
|
+
# through a compression algorithm would make them larger.
|
66
|
+
#
|
67
|
+
# Return Boolean.
|
68
|
+
def can_compress?
|
69
|
+
# The "charset" of a mime type is present if the value is
|
70
|
+
# encoded text. We can check this value to see if the asset
|
71
|
+
# can be compressed.
|
72
|
+
#
|
73
|
+
# We also check against our list of non-text compressible mime types
|
74
|
+
@charset || COMPRESSABLE_MIME_TYPES.include?(@content_type)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Private: Opposite of `can_compress?`.
|
78
|
+
#
|
79
|
+
# Returns Boolean.
|
80
|
+
def cannot_compress?
|
81
|
+
!can_compress?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Private: Generates a gzipped file based off of reference asset.
|
85
|
+
#
|
86
|
+
# Compresses the target asset's contents and puts it into a file with
|
87
|
+
# the same name plus a `.gz` extension in the same folder as the original.
|
88
|
+
# Does not modify the target asset.
|
89
|
+
#
|
90
|
+
# Returns nothing.
|
91
|
+
def compress(file, target)
|
92
|
+
mtime = Sprockets::PathUtils.stat(target).mtime
|
93
|
+
archiver.call(file, source, mtime)
|
94
|
+
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/sprockets/utils.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
module Sprockets
|
@@ -45,7 +46,8 @@ module Sprockets
|
|
45
46
|
# Similar to Hash#store for nested frozen hashes.
|
46
47
|
#
|
47
48
|
# hash - Hash
|
48
|
-
#
|
49
|
+
# key_a - Object key. Use multiple keys for nested hashes.
|
50
|
+
# key_b - Object key. Use multiple keys for nested hashes.
|
49
51
|
# block - Receives current value at key.
|
50
52
|
#
|
51
53
|
# Examples
|
@@ -56,16 +58,19 @@ module Sprockets
|
|
56
58
|
# end
|
57
59
|
#
|
58
60
|
# Returns duplicated frozen Hash.
|
59
|
-
def hash_reassoc(hash,
|
60
|
-
if
|
61
|
-
hash_reassoc1(hash,
|
62
|
-
|
63
|
-
hash_reassoc1(hash, keys[0]) do |value|
|
64
|
-
hash_reassoc(value, *keys[1..-1], &block)
|
61
|
+
def hash_reassoc(hash, key_a, key_b = nil, &block)
|
62
|
+
if key_b
|
63
|
+
hash_reassoc1(hash, key_a) do |value|
|
64
|
+
hash_reassoc(value, key_b, &block)
|
65
65
|
end
|
66
|
+
else
|
67
|
+
hash_reassoc1(hash, key_a, &block)
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
71
|
+
WHITESPACE_ORDINALS = {0x0A => "\n", 0x20 => " ", 0x09 => "\t"}
|
72
|
+
private_constant :WHITESPACE_ORDINALS
|
73
|
+
|
69
74
|
# Internal: Check if string has a trailing semicolon.
|
70
75
|
#
|
71
76
|
# str - String
|
@@ -74,60 +79,43 @@ module Sprockets
|
|
74
79
|
def string_end_with_semicolon?(str)
|
75
80
|
i = str.size - 1
|
76
81
|
while i >= 0
|
77
|
-
c = str[i]
|
82
|
+
c = str[i].ord
|
78
83
|
i -= 1
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
else
|
84
|
-
return true
|
85
|
-
end
|
84
|
+
|
85
|
+
next if WHITESPACE_ORDINALS[c]
|
86
|
+
|
87
|
+
return c === 0x3B
|
86
88
|
end
|
89
|
+
|
87
90
|
true
|
88
91
|
end
|
89
92
|
|
90
93
|
# Internal: Accumulate asset source to buffer and append a trailing
|
91
94
|
# semicolon if necessary.
|
92
95
|
#
|
93
|
-
# buf
|
94
|
-
#
|
96
|
+
# buf - String buffer to append to
|
97
|
+
# source - String source to append
|
95
98
|
#
|
96
|
-
# Returns
|
99
|
+
# Returns buf String.
|
97
100
|
def concat_javascript_sources(buf, source)
|
98
|
-
if
|
99
|
-
|
101
|
+
return buf if source.bytesize <= 0
|
102
|
+
|
103
|
+
buf << source
|
104
|
+
# If the source contains non-ASCII characters, indexing on it becomes O(N).
|
105
|
+
# This will lead to O(N^2) performance in string_end_with_semicolon?, so we should use 32 bit encoding to make sure indexing stays O(1)
|
106
|
+
source = source.encode(Encoding::UTF_32LE) unless source.ascii_only?
|
107
|
+
return buf if string_end_with_semicolon?(source)
|
108
|
+
|
109
|
+
# If the last character in the string was whitespace,
|
110
|
+
# such as a newline, then we want to put the semicolon
|
111
|
+
# before the whitespace. Otherwise append a semicolon.
|
112
|
+
if whitespace = WHITESPACE_ORDINALS[source[-1].ord]
|
113
|
+
buf[-1] = ";#{whitespace}"
|
100
114
|
else
|
101
|
-
buf
|
115
|
+
buf << ";"
|
102
116
|
end
|
103
|
-
end
|
104
117
|
|
105
|
-
|
106
|
-
#
|
107
|
-
# normalize_extension("js")
|
108
|
-
# # => ".js"
|
109
|
-
#
|
110
|
-
# normalize_extension(".css")
|
111
|
-
# # => ".css"
|
112
|
-
#
|
113
|
-
def normalize_extension(extension)
|
114
|
-
extension = extension.to_s
|
115
|
-
if extension[/^\./]
|
116
|
-
extension
|
117
|
-
else
|
118
|
-
".#{extension}"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# Internal: Feature detect if UnboundMethods can #bind to any Object or
|
123
|
-
# just Objects that share the same super class.
|
124
|
-
# Basically if RUBY_VERSION >= 2.
|
125
|
-
UNBOUND_METHODS_BIND_TO_ANY_OBJECT = begin
|
126
|
-
foo = Module.new { def bar; end }
|
127
|
-
foo.instance_method(:bar).bind(Object.new)
|
128
|
-
true
|
129
|
-
rescue TypeError
|
130
|
-
false
|
118
|
+
buf
|
131
119
|
end
|
132
120
|
|
133
121
|
# Internal: Inject into target module for the duration of the block.
|
@@ -142,10 +130,6 @@ module Sprockets
|
|
142
130
|
old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
|
143
131
|
end
|
144
132
|
|
145
|
-
unless UNBOUND_METHODS_BIND_TO_ANY_OBJECT
|
146
|
-
base.send(:include, mod) unless base < mod
|
147
|
-
end
|
148
|
-
|
149
133
|
mod.instance_methods.each do |sym|
|
150
134
|
method = mod.instance_method(sym)
|
151
135
|
base.send(:define_method, sym, method)
|
@@ -199,16 +183,16 @@ module Sprockets
|
|
199
183
|
# Returns an Array of node Arrays.
|
200
184
|
def dfs_paths(path)
|
201
185
|
paths = []
|
202
|
-
stack
|
186
|
+
stack = [path]
|
187
|
+
seen = Set.new
|
203
188
|
|
204
189
|
while path = stack.pop
|
205
|
-
|
206
|
-
|
207
|
-
paths << path if path.size > 1
|
190
|
+
seen.add(path.last)
|
191
|
+
paths << path
|
208
192
|
|
209
|
-
|
210
|
-
|
211
|
-
|
193
|
+
children = yield path.last
|
194
|
+
children.reverse_each do |node|
|
195
|
+
stack.push(path + [node]) unless seen.include?(node)
|
212
196
|
end
|
213
197
|
end
|
214
198
|
|
data/lib/sprockets/version.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'sprockets/autoload'
|
3
|
+
require 'sprockets/digest_utils'
|
2
4
|
|
3
5
|
module Sprockets
|
4
6
|
# Public: YUI compressor.
|
@@ -35,12 +37,7 @@ module Sprockets
|
|
35
37
|
|
36
38
|
def initialize(options = {})
|
37
39
|
@options = options
|
38
|
-
@cache_key =
|
39
|
-
self.class.name,
|
40
|
-
Autoload::YUI::Compressor::VERSION,
|
41
|
-
VERSION,
|
42
|
-
options
|
43
|
-
].freeze
|
40
|
+
@cache_key = "#{self.class.name}:#{Autoload::YUI::Compressor::VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
|
44
41
|
end
|
45
42
|
|
46
43
|
def call(input)
|
@@ -48,15 +45,9 @@ module Sprockets
|
|
48
45
|
|
49
46
|
case input[:content_type]
|
50
47
|
when 'application/javascript'
|
51
|
-
|
52
|
-
input[:cache].fetch(key) do
|
53
|
-
Autoload::YUI::JavaScriptCompressor.new(@options).compress(data)
|
54
|
-
end
|
48
|
+
Autoload::YUI::JavaScriptCompressor.new(@options).compress(data)
|
55
49
|
when 'text/css'
|
56
|
-
|
57
|
-
input[:cache].fetch(key) do
|
58
|
-
Autoload::YUI::CssCompressor.new(@options).compress(data)
|
59
|
-
end
|
50
|
+
Autoload::YUI::CssCompressor.new(@options).compress(data)
|
60
51
|
else
|
61
52
|
data
|
62
53
|
end
|