sprockets 3.0.0 → 4.0.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 +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
|