sprockets 2.12.5 → 3.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprockets might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/LICENSE +2 -2
- data/README.md +61 -34
- data/lib/rake/sprocketstask.rb +5 -4
- data/lib/sprockets.rb +123 -85
- data/lib/sprockets/asset.rb +161 -200
- data/lib/sprockets/asset_uri.rb +64 -0
- data/lib/sprockets/base.rb +138 -373
- data/lib/sprockets/bower.rb +56 -0
- data/lib/sprockets/bundle.rb +32 -0
- data/lib/sprockets/cache.rb +220 -0
- data/lib/sprockets/cache/file_store.rb +145 -13
- data/lib/sprockets/cache/memory_store.rb +66 -0
- data/lib/sprockets/cache/null_store.rb +46 -0
- data/lib/sprockets/cached_environment.rb +103 -0
- data/lib/sprockets/closure_compressor.rb +30 -12
- data/lib/sprockets/coffee_script_template.rb +23 -0
- data/lib/sprockets/compressing.rb +20 -25
- data/lib/sprockets/configuration.rb +95 -0
- data/lib/sprockets/context.rb +68 -131
- data/lib/sprockets/directive_processor.rb +138 -179
- data/lib/sprockets/eco_template.rb +10 -19
- data/lib/sprockets/ejs_template.rb +10 -19
- data/lib/sprockets/encoding_utils.rb +246 -0
- data/lib/sprockets/engines.rb +40 -29
- data/lib/sprockets/environment.rb +10 -66
- data/lib/sprockets/erb_template.rb +23 -0
- data/lib/sprockets/errors.rb +5 -13
- data/lib/sprockets/http_utils.rb +97 -0
- data/lib/sprockets/jst_processor.rb +28 -15
- data/lib/sprockets/lazy_processor.rb +15 -0
- data/lib/sprockets/legacy.rb +23 -0
- data/lib/sprockets/legacy_proc_processor.rb +35 -0
- data/lib/sprockets/legacy_tilt_processor.rb +29 -0
- data/lib/sprockets/manifest.rb +128 -99
- data/lib/sprockets/mime.rb +114 -33
- data/lib/sprockets/path_utils.rb +179 -0
- data/lib/sprockets/paths.rb +13 -26
- data/lib/sprockets/processing.rb +198 -107
- data/lib/sprockets/resolve.rb +289 -0
- data/lib/sprockets/sass_compressor.rb +36 -17
- data/lib/sprockets/sass_template.rb +269 -46
- data/lib/sprockets/server.rb +113 -83
- data/lib/sprockets/transformers.rb +69 -0
- data/lib/sprockets/uglifier_compressor.rb +36 -15
- data/lib/sprockets/utils.rb +161 -44
- data/lib/sprockets/version.rb +1 -1
- data/lib/sprockets/yui_compressor.rb +37 -12
- metadata +64 -106
- data/lib/sprockets/asset_attributes.rb +0 -137
- data/lib/sprockets/bundled_asset.rb +0 -78
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- data/lib/sprockets/index.rb +0 -100
- data/lib/sprockets/processed_asset.rb +0 -152
- data/lib/sprockets/processor.rb +0 -32
- data/lib/sprockets/safety_colons.rb +0 -28
- data/lib/sprockets/sass_cache_store.rb +0 -29
- data/lib/sprockets/sass_functions.rb +0 -70
- data/lib/sprockets/sass_importer.rb +0 -30
- data/lib/sprockets/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -60
@@ -0,0 +1,66 @@
|
|
1
|
+
module Sprockets
|
2
|
+
class Cache
|
3
|
+
# Public: Basic in memory LRU cache.
|
4
|
+
#
|
5
|
+
# Assign the instance to the Environment#cache.
|
6
|
+
#
|
7
|
+
# environment.cache = Sprockets::Cache::MemoryStore.new(1000)
|
8
|
+
#
|
9
|
+
# See Also
|
10
|
+
#
|
11
|
+
# ActiveSupport::Cache::MemoryStore
|
12
|
+
#
|
13
|
+
class MemoryStore
|
14
|
+
# Internal: Default key limit for store.
|
15
|
+
DEFAULT_MAX_SIZE = 1000
|
16
|
+
|
17
|
+
# Public: Initialize the cache store.
|
18
|
+
#
|
19
|
+
# max_size - A Integer of the maximum number of keys the store will hold.
|
20
|
+
# (default: 1000).
|
21
|
+
def initialize(max_size = DEFAULT_MAX_SIZE)
|
22
|
+
@max_size = max_size
|
23
|
+
@cache = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Retrieve value from cache.
|
27
|
+
#
|
28
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
29
|
+
#
|
30
|
+
# key - String cache key.
|
31
|
+
#
|
32
|
+
# Returns Object or nil or the value is not set.
|
33
|
+
def get(key)
|
34
|
+
exists = true
|
35
|
+
value = @cache.delete(key) { exists = false }
|
36
|
+
if exists
|
37
|
+
@cache[key] = value
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Set a key and value in the cache.
|
44
|
+
#
|
45
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
46
|
+
#
|
47
|
+
# key - String cache key.
|
48
|
+
# value - Object value.
|
49
|
+
#
|
50
|
+
# Returns Object value.
|
51
|
+
def set(key, value)
|
52
|
+
@cache.delete(key)
|
53
|
+
@cache[key] = value
|
54
|
+
@cache.shift if @cache.size > @max_size
|
55
|
+
value
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Pretty inspect
|
59
|
+
#
|
60
|
+
# Returns String.
|
61
|
+
def inspect
|
62
|
+
"#<#{self.class} size=#{@cache.size}/#{@max_size}>"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Sprockets
|
2
|
+
class Cache
|
3
|
+
# Public: A compatible cache store that doesn't store anything. Used by
|
4
|
+
# default when no Environment#cache is configured.
|
5
|
+
#
|
6
|
+
# Assign the instance to the Environment#cache.
|
7
|
+
#
|
8
|
+
# environment.cache = Sprockets::Cache::NullStore.new
|
9
|
+
#
|
10
|
+
# See Also
|
11
|
+
#
|
12
|
+
# ActiveSupport::Cache::NullStore
|
13
|
+
#
|
14
|
+
class NullStore
|
15
|
+
# Public: Simulate a cache miss.
|
16
|
+
#
|
17
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
18
|
+
#
|
19
|
+
# key - String cache key.
|
20
|
+
#
|
21
|
+
# Returns nil.
|
22
|
+
def get(key)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Simulate setting a value in the cache.
|
27
|
+
#
|
28
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
29
|
+
#
|
30
|
+
# key - String cache key.
|
31
|
+
# value - Object value.
|
32
|
+
#
|
33
|
+
# Returns Object value.
|
34
|
+
def set(key, value)
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Pretty inspect
|
39
|
+
#
|
40
|
+
# Returns String.
|
41
|
+
def inspect
|
42
|
+
"#<#{self.class}>"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'sprockets/base'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# `Cached` is a special cached version of `Environment`.
|
5
|
+
#
|
6
|
+
# The expection is that all of its file system methods are cached
|
7
|
+
# for the instances lifetime. This makes `Cached` much faster. This
|
8
|
+
# behavior is ideal in production environments where the file system
|
9
|
+
# is immutable.
|
10
|
+
#
|
11
|
+
# `Cached` should not be initialized directly. Instead use
|
12
|
+
# `Environment#cached`.
|
13
|
+
class CachedEnvironment < Base
|
14
|
+
def initialize(environment)
|
15
|
+
initialize_configuration(environment)
|
16
|
+
|
17
|
+
@cache = environment.cache
|
18
|
+
@stats = Hash.new { |h, k| h[k] = _stat(k) }
|
19
|
+
@entries = Hash.new { |h, k| h[k] = _entries(k) }
|
20
|
+
@hexdigests = Hash.new { |h, k| h[k] = _file_hexdigest(k) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# No-op return self as cached environment.
|
24
|
+
def cached
|
25
|
+
self
|
26
|
+
end
|
27
|
+
alias_method :index, :cached
|
28
|
+
|
29
|
+
# Internal: Cache Environment#entries
|
30
|
+
alias_method :_entries, :entries
|
31
|
+
def entries(path)
|
32
|
+
@entries[path]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Internal: Cache Environment#stat
|
36
|
+
alias_method :_stat, :stat
|
37
|
+
def stat(path)
|
38
|
+
@stats[path]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal: Cache Environment#file_hexdigest
|
42
|
+
alias_method :_file_hexdigest, :file_hexdigest
|
43
|
+
def file_hexdigest(path)
|
44
|
+
@hexdigests[path]
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def asset_dependency_graph_cache_key(uri)
|
49
|
+
filename, _ = AssetURI.parse(uri)
|
50
|
+
[
|
51
|
+
'asset-uri-dep-graph',
|
52
|
+
VERSION,
|
53
|
+
self.version,
|
54
|
+
self.paths,
|
55
|
+
uri,
|
56
|
+
file_hexdigest(filename)
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
def asset_digest_uri_cache_key(uri)
|
61
|
+
[
|
62
|
+
'asset-digest-uri',
|
63
|
+
VERSION,
|
64
|
+
self.version,
|
65
|
+
uri
|
66
|
+
]
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_asset_by_digest_uri(uri)
|
70
|
+
cache.fetch(asset_digest_uri_cache_key(uri)) do
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_asset_by_uri(uri)
|
76
|
+
dep_graph_key = asset_dependency_graph_cache_key(uri)
|
77
|
+
|
78
|
+
dependency_paths, dependency_digest, digest_uri = cache._get(dep_graph_key)
|
79
|
+
if dependency_paths && dependency_digest && digest_uri
|
80
|
+
if dependencies_hexdigest(dependency_paths) == dependency_digest
|
81
|
+
if asset = cache._get(asset_digest_uri_cache_key(digest_uri))
|
82
|
+
return asset
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
asset = super
|
88
|
+
|
89
|
+
dependency_digest, dependency_paths = asset[:metadata].values_at(:dependency_digest, :dependency_paths)
|
90
|
+
cache._set(dep_graph_key, [dependency_paths, dependency_digest, asset[:uri]])
|
91
|
+
cache.fetch(asset_digest_uri_cache_key(asset[:uri])) { asset }
|
92
|
+
|
93
|
+
asset
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
# Cache is immutable, any methods that try to clear the cache
|
98
|
+
# should bomb.
|
99
|
+
def mutate_config(*args)
|
100
|
+
raise RuntimeError, "can't modify immutable cached environment"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -1,22 +1,40 @@
|
|
1
|
-
require '
|
1
|
+
require 'closure-compiler'
|
2
2
|
|
3
3
|
module Sprockets
|
4
|
-
|
5
|
-
|
4
|
+
# Public: Closure Compiler minifier.
|
5
|
+
#
|
6
|
+
# To accept the default options
|
7
|
+
#
|
8
|
+
# environment.register_bundle_processor 'application/javascript',
|
9
|
+
# Sprockets::ClosureCompressor
|
10
|
+
#
|
11
|
+
# Or to pass options to the Closure::Compiler class.
|
12
|
+
#
|
13
|
+
# environment.register_bundle_processor 'application/javascript',
|
14
|
+
# Sprockets::ClosureCompressor.new({ ... })
|
15
|
+
#
|
16
|
+
class ClosureCompressor
|
17
|
+
VERSION = '1'
|
6
18
|
|
7
|
-
def self.
|
8
|
-
|
19
|
+
def self.call(*args)
|
20
|
+
new.call(*args)
|
9
21
|
end
|
10
22
|
|
11
|
-
def
|
12
|
-
|
23
|
+
def initialize(options = {})
|
24
|
+
@compiler = ::Closure::Compiler.new(options)
|
25
|
+
@cache_key = [
|
26
|
+
'ClosureCompressor',
|
27
|
+
::Closure::VERSION,
|
28
|
+
::Closure::COMPILER_VERSION,
|
29
|
+
VERSION,
|
30
|
+
options
|
31
|
+
]
|
13
32
|
end
|
14
33
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
Closure::Compiler.new.compile(data)
|
34
|
+
def call(input)
|
35
|
+
input[:cache].fetch(@cache_key + [input[:data]]) do
|
36
|
+
@compiler.compile(input[:data])
|
37
|
+
end
|
20
38
|
end
|
21
39
|
end
|
22
40
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'coffee_script'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# Template engine class for the CoffeeScript compiler.
|
5
|
+
# Depends on the `coffee-script` and `coffee-script-source` gems.
|
6
|
+
#
|
7
|
+
# For more infomation see:
|
8
|
+
#
|
9
|
+
# https://github.com/josh/ruby-coffee-script
|
10
|
+
#
|
11
|
+
module CoffeeScriptTemplate
|
12
|
+
VERSION = '1'
|
13
|
+
SOURCE_VERSION = ::CoffeeScript::Source.version
|
14
|
+
|
15
|
+
def self.call(input)
|
16
|
+
data = input[:data]
|
17
|
+
key = ['CoffeeScriptTemplate', SOURCE_VERSION, VERSION, data]
|
18
|
+
input[:cache].fetch(key) do
|
19
|
+
::CoffeeScript.compile(data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,37 +1,35 @@
|
|
1
1
|
module Sprockets
|
2
2
|
# `Compressing` is an internal mixin whose public methods are exposed on
|
3
|
-
# the `Environment` and `
|
3
|
+
# the `Environment` and `CachedEnvironment` classes.
|
4
4
|
module Compressing
|
5
|
-
|
6
|
-
deep_copy_hash(@compressors)
|
7
|
-
end
|
5
|
+
attr_reader :compressors
|
8
6
|
|
9
7
|
def register_compressor(mime_type, sym, klass)
|
10
|
-
|
8
|
+
mutate_hash_config(:compressors, mime_type) do |compressors|
|
9
|
+
compressors[sym] = klass
|
10
|
+
compressors
|
11
|
+
end
|
11
12
|
end
|
12
13
|
|
13
14
|
# Return CSS compressor or nil if none is set
|
14
15
|
def css_compressor
|
15
|
-
|
16
|
+
if defined? @css_compressor
|
17
|
+
unwrap_processor(@css_compressor)
|
18
|
+
end
|
16
19
|
end
|
17
20
|
|
18
21
|
# Assign a compressor to run on `text/css` assets.
|
19
22
|
#
|
20
23
|
# The compressor object must respond to `compress`.
|
21
24
|
def css_compressor=(compressor)
|
22
|
-
unregister_bundle_processor 'text/css', css_compressor if css_compressor
|
25
|
+
unregister_bundle_processor 'text/css', @css_compressor if defined? @css_compressor
|
23
26
|
@css_compressor = nil
|
24
27
|
return unless compressor
|
25
28
|
|
26
29
|
if compressor.is_a?(Symbol)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if compressor.respond_to?(:compress)
|
31
|
-
klass = Class.new(Processor) do
|
32
|
-
@name = "css_compressor"
|
33
|
-
@processor = proc { |context, data| compressor.compress(data) }
|
34
|
-
end
|
30
|
+
@css_compressor = klass = compressors['text/css'][compressor] || raise(Error, "unknown compressor: #{compressor}")
|
31
|
+
elsif compressor.respond_to?(:compress)
|
32
|
+
klass = LegacyProcProcessor.new(:css_compressor, proc { |context, data| compressor.compress(data) })
|
35
33
|
@css_compressor = :css_compressor
|
36
34
|
else
|
37
35
|
@css_compressor = klass = compressor
|
@@ -42,26 +40,23 @@ module Sprockets
|
|
42
40
|
|
43
41
|
# Return JS compressor or nil if none is set
|
44
42
|
def js_compressor
|
45
|
-
|
43
|
+
if defined? @js_compressor
|
44
|
+
unwrap_processor(@js_compressor)
|
45
|
+
end
|
46
46
|
end
|
47
47
|
|
48
48
|
# Assign a compressor to run on `application/javascript` assets.
|
49
49
|
#
|
50
50
|
# The compressor object must respond to `compress`.
|
51
51
|
def js_compressor=(compressor)
|
52
|
-
unregister_bundle_processor 'application/javascript', js_compressor if js_compressor
|
52
|
+
unregister_bundle_processor 'application/javascript', @js_compressor if defined? @js_compressor
|
53
53
|
@js_compressor = nil
|
54
54
|
return unless compressor
|
55
55
|
|
56
56
|
if compressor.is_a?(Symbol)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
if compressor.respond_to?(:compress)
|
61
|
-
klass = Class.new(Processor) do
|
62
|
-
@name = "js_compressor"
|
63
|
-
@processor = proc { |context, data| compressor.compress(data) }
|
64
|
-
end
|
57
|
+
@js_compressor = klass = compressors['application/javascript'][compressor] || raise(Error, "unknown compressor: #{compressor}")
|
58
|
+
elsif compressor.respond_to?(:compress)
|
59
|
+
klass = LegacyProcProcessor.new(:js_compressor, proc { |context, data| compressor.compress(data) })
|
65
60
|
@js_compressor = :js_compressor
|
66
61
|
else
|
67
62
|
@js_compressor = klass = compressor
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'sprockets/compressing'
|
2
|
+
require 'sprockets/engines'
|
3
|
+
require 'sprockets/mime'
|
4
|
+
require 'sprockets/paths'
|
5
|
+
require 'sprockets/processing'
|
6
|
+
require 'sprockets/transformers'
|
7
|
+
|
8
|
+
module Sprockets
|
9
|
+
module Configuration
|
10
|
+
include Paths, Mime, Engines, Transformers, Processing, Compressing
|
11
|
+
|
12
|
+
def initialize_configuration(parent)
|
13
|
+
@logger = parent.logger
|
14
|
+
@version = parent.version
|
15
|
+
@digest_class = parent.digest_class
|
16
|
+
@context_class = Class.new(parent.context_class)
|
17
|
+
@root = parent.root
|
18
|
+
@paths = parent.paths
|
19
|
+
@mime_types = parent.mime_types
|
20
|
+
@mime_exts = parent.mime_exts
|
21
|
+
@encodings = parent.encodings
|
22
|
+
@engines = parent.engines
|
23
|
+
@engine_mime_types = parent.engine_mime_types
|
24
|
+
@transformers = parent.transformers
|
25
|
+
@preprocessors = parent.preprocessors
|
26
|
+
@postprocessors = parent.postprocessors
|
27
|
+
@bundle_reducers = parent.bundle_reducers
|
28
|
+
@bundle_processors = parent.bundle_processors
|
29
|
+
@compressors = parent.compressors
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get and set `Logger` instance.
|
33
|
+
attr_accessor :logger
|
34
|
+
|
35
|
+
# The `Environment#version` is a custom value used for manually
|
36
|
+
# expiring all asset caches.
|
37
|
+
#
|
38
|
+
# Sprockets is able to track most file and directory changes and
|
39
|
+
# will take care of expiring the cache for you. However, its
|
40
|
+
# impossible to know when any custom helpers change that you mix
|
41
|
+
# into the `Context`.
|
42
|
+
#
|
43
|
+
# It would be wise to increment this value anytime you make a
|
44
|
+
# configuration change to the `Environment` object.
|
45
|
+
attr_reader :version
|
46
|
+
|
47
|
+
# Assign an environment version.
|
48
|
+
#
|
49
|
+
# environment.version = '2.0'
|
50
|
+
#
|
51
|
+
def version=(version)
|
52
|
+
mutate_config(:version) { version.dup }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a `Digest` implementation class.
|
56
|
+
#
|
57
|
+
# Defaults to `Digest::SHA1`.
|
58
|
+
attr_reader :digest_class
|
59
|
+
|
60
|
+
# Assign a `Digest` implementation class. This maybe any Ruby
|
61
|
+
# `Digest::` implementation such as `Digest::SHA1` or
|
62
|
+
# `Digest::MD5`.
|
63
|
+
#
|
64
|
+
# environment.digest_class = Digest::MD5
|
65
|
+
#
|
66
|
+
def digest_class=(klass)
|
67
|
+
@digest_class = klass
|
68
|
+
end
|
69
|
+
|
70
|
+
# Deprecated: Get `Context` class.
|
71
|
+
#
|
72
|
+
# This class maybe mutated and mixed in with custom helpers.
|
73
|
+
#
|
74
|
+
# environment.context_class.instance_eval do
|
75
|
+
# include MyHelpers
|
76
|
+
# def asset_url; end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
attr_reader :context_class
|
80
|
+
|
81
|
+
private
|
82
|
+
def mutate_config(sym)
|
83
|
+
obj = yield self.instance_variable_get("@#{sym}").dup
|
84
|
+
self.instance_variable_set("@#{sym}", obj.freeze)
|
85
|
+
end
|
86
|
+
|
87
|
+
def mutate_hash_config(sym, key)
|
88
|
+
mutate_config(sym) do |hash|
|
89
|
+
obj = yield hash[key].dup
|
90
|
+
hash[key] = obj.freeze
|
91
|
+
hash
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|