sprockets-sass 1.3.1 → 2.0.0.beta2
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 +7 -0
- data/.reek +12 -0
- data/.rspec +1 -0
- data/.rubocop.yml +77 -0
- data/.travis.yml +14 -0
- data/Appraisals +32 -15
- data/Gemfile +5 -0
- data/README.md +41 -4
- data/Rakefile +15 -3
- data/gemfiles/compass_0.11.gemfile +1 -1
- data/gemfiles/sass_3.1.gemfile +1 -1
- data/gemfiles/sass_3.3.gemfile +8 -0
- data/gemfiles/sass_3.4.gemfile +8 -0
- data/gemfiles/sprockets_2.10.gemfile +1 -1
- data/gemfiles/sprockets_2.11.gemfile +1 -1
- data/gemfiles/sprockets_2.2.gemfile +1 -1
- data/gemfiles/sprockets_2.3.gemfile +1 -1
- data/gemfiles/sprockets_2.4.gemfile +1 -1
- data/gemfiles/sprockets_2.5.gemfile +1 -1
- data/gemfiles/sprockets_2.6.gemfile +1 -1
- data/gemfiles/sprockets_2.7.gemfile +1 -1
- data/gemfiles/sprockets_2.8.gemfile +1 -1
- data/gemfiles/sprockets_2.9.gemfile +1 -1
- data/gemfiles/sprockets_3.7.gemfile +9 -0
- data/gemfiles/sprockets_4.0.gemfile +10 -0
- data/gemfiles/sprockets_4.0_beta2.gemfile +9 -0
- data/lib/sprockets/sass/functions.rb +24 -159
- data/lib/sprockets/sass/registration.rb +126 -0
- data/lib/sprockets/sass/utils.rb +115 -0
- data/lib/sprockets/sass/v2/cache_store.rb +26 -0
- data/lib/sprockets/sass/v2/compressor.rb +31 -0
- data/lib/sprockets/sass/v2/functions.rb +142 -0
- data/lib/sprockets/sass/v2/importer.rb +209 -0
- data/lib/sprockets/sass/v2/sass_template.rb +221 -0
- data/lib/sprockets/sass/v2/scss_template.rb +14 -0
- data/lib/sprockets/sass/v3/cache_store.rb +28 -0
- data/lib/sprockets/sass/v3/compressor.rb +97 -0
- data/lib/sprockets/sass/v3/functions.rb +12 -0
- data/lib/sprockets/sass/v3/importer.rb +224 -0
- data/lib/sprockets/sass/v3/sass_template.rb +37 -0
- data/lib/sprockets/sass/v3/scss_template.rb +15 -0
- data/lib/sprockets/sass/v4/cache_store.rb +11 -0
- data/lib/sprockets/sass/v4/compressor.rb +11 -0
- data/lib/sprockets/sass/v4/functions.rb +12 -0
- data/lib/sprockets/sass/v4/importer.rb +109 -0
- data/lib/sprockets/sass/v4/sass_template.rb +27 -0
- data/lib/sprockets/sass/v4/scss_template.rb +16 -0
- data/lib/sprockets/sass/version.rb +2 -1
- data/lib/sprockets/sass.rb +37 -12
- data/lib/sprockets-sass.rb +1 -0
- data/spec/custom_importer_spec.rb +4 -6
- data/spec/importer_spec.rb +44 -0
- data/spec/spec_helper.rb +39 -3
- data/spec/sprockets-sass_spec.rb +102 -62
- data/spec/support/be_fresh_matcher.rb +12 -8
- data/spec/support/dummy_importer.rb +1 -1
- data/spec/support/fail_postprocessor.rb +23 -0
- data/spec/support/fake_engine.rb +50 -0
- data/spec/support/sass_template.rb +11 -0
- data/spec/support/test_registration.rb +11 -0
- data/sprockets-sass.gemspec +28 -9
- metadata +100 -97
- data/gemfiles/compass_0.12.gemfile +0 -7
- data/gemfiles/sass_3.2.gemfile +0 -7
- data/gemfiles/sprockets_2.0.gemfile +0 -7
- data/gemfiles/sprockets_2.1.gemfile +0 -9
- data/lib/sprockets/sass/cache_store.rb +0 -27
- data/lib/sprockets/sass/compressor.rb +0 -22
- data/lib/sprockets/sass/importer.rb +0 -142
- data/lib/sprockets/sass/sass_template.rb +0 -115
- data/lib/sprockets/sass/scss_template.rb +0 -12
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
module Sass
|
|
4
|
+
# class useful for registering engines, tranformers, preprocessors and conpressors for sprockets
|
|
5
|
+
# depending on the version of sprockets
|
|
6
|
+
class Registration
|
|
7
|
+
attr_reader :klass, :sprockets_version, :registration_instance
|
|
8
|
+
|
|
9
|
+
def initialize(klass)
|
|
10
|
+
@klass = klass
|
|
11
|
+
@sprockets_version = Sprockets::Sass::Utils.version_of_sprockets
|
|
12
|
+
@registration_instance = self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
require_libraries
|
|
17
|
+
case @sprockets_version
|
|
18
|
+
when 2...3
|
|
19
|
+
register_sprockets_legacy
|
|
20
|
+
when 3...4
|
|
21
|
+
register_sprockets_v3
|
|
22
|
+
when 4...5
|
|
23
|
+
register_sprockets_v4
|
|
24
|
+
else
|
|
25
|
+
raise "Version #{Sprockets::Sass::Utils.full_version_of_sprockets} is not supported"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def require_libraries
|
|
30
|
+
require_standard_libraries
|
|
31
|
+
require 'sprockets/sass/functions'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def require_standard_libraries(version = @sprockets_version)
|
|
37
|
+
%w(cache_store compressor functions importer sass_template scss_template).each do |filename|
|
|
38
|
+
begin
|
|
39
|
+
require "sprockets/sass/v#{version}/#{filename}"
|
|
40
|
+
rescue LoadError; end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def register_sprockets_v3_common
|
|
45
|
+
%w(sass scss).each do |mime|
|
|
46
|
+
_register_mime_types(mime_type: "application/#{mime}+ruby", extensions: [".#{mime}.erb", ".css.#{mime}.erb"])
|
|
47
|
+
end
|
|
48
|
+
_register_compressors(mime_type: 'text/css', name: :sprockets_sass, klass: Sprockets::Sass::Utils.get_class_by_version('Compressor'))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def register_sprockets_v4
|
|
52
|
+
register_sprockets_v3_common
|
|
53
|
+
%w(sass scss).each do |mime|
|
|
54
|
+
_register_mime_types(mime_type: "text/#{mime}", extensions: [".#{mime}", ".css.#{mime}"])
|
|
55
|
+
end
|
|
56
|
+
%w(sass scss).each do |mime|
|
|
57
|
+
_register_transformers(from: "application/#{mime}+ruby", to: "text/#{mime}", klass: Sprockets::ERBProcessor)
|
|
58
|
+
end
|
|
59
|
+
_register_v4_preprocessors(
|
|
60
|
+
Sprockets::Sass::V4::SassTemplate => ['text/sass'],
|
|
61
|
+
Sprockets::Sass::V4::ScssTemplate => ['text/scss']
|
|
62
|
+
)
|
|
63
|
+
_register_transformers(
|
|
64
|
+
{ from: 'text/sass', to: 'text/css', klass: Sprockets::Sass::V4::SassTemplate },
|
|
65
|
+
from: 'text/scss', to: 'text/css', klass: Sprockets::Sass::V4::ScssTemplate
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def register_sprockets_v3
|
|
70
|
+
register_sprockets_v3_common
|
|
71
|
+
_register_transformers(
|
|
72
|
+
{ from: 'application/scss+ruby', to: 'text/css', klass: Sprockets::ERBProcessor },
|
|
73
|
+
from: 'application/sass+ruby', to: 'text/css', klass: Sprockets::ERBProcessor
|
|
74
|
+
)
|
|
75
|
+
_register_engines('.sass' => Sprockets::Sass::V3::SassTemplate, '.scss' => Sprockets::Sass::V3::ScssTemplate)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def register_sprockets_legacy
|
|
79
|
+
_register_engines('.sass' => Sprockets::Sass::V2::SassTemplate, '.scss' => Sprockets::Sass::V2::ScssTemplate)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def _register_engines(hash)
|
|
83
|
+
hash.each do |key, value|
|
|
84
|
+
args = [key, value]
|
|
85
|
+
args << { mime_type: 'text/css', silence_deprecation: true } if sprockets_version >= 3
|
|
86
|
+
register_engine(*args)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def _register_mime_types(*mime_types)
|
|
91
|
+
mime_types.each do |mime_data|
|
|
92
|
+
register_mime_type(mime_data[:mime_type], extensions: mime_data[:extensions])
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def _register_compressors(*compressors)
|
|
97
|
+
compressors.each do |compressor|
|
|
98
|
+
register_compressor(compressor[:mime_type], compressor[:name], compressor[:klass])
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def _register_transformers(*tranformers)
|
|
103
|
+
tranformers.each do |tranformer|
|
|
104
|
+
register_transformer(tranformer[:from], tranformer[:to], tranformer[:klass])
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def _register_v4_preprocessors(hash)
|
|
109
|
+
hash.each do |key, value|
|
|
110
|
+
value.each do |mime|
|
|
111
|
+
register_preprocessor(mime, key)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def method_missing(sym, *args, &block)
|
|
117
|
+
@klass.public_send(sym, *args, &block) || super
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def respond_to_missing?(method_name, include_private = nil)
|
|
121
|
+
include_private = include_private.blank? ? true : include_private
|
|
122
|
+
@klass.public_methods.include?(method_name) || super(method_name, include_private)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
module Sass
|
|
4
|
+
# utility functions that can be used statically from anywhere
|
|
5
|
+
class Utils
|
|
6
|
+
class << self
|
|
7
|
+
def full_version_of_sprockets
|
|
8
|
+
Sprockets::VERSION
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def version_of_sprockets
|
|
12
|
+
full_version_of_sprockets.split('.')[0].to_i
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def read_file_binary(file, options = {})
|
|
16
|
+
default_encoding = options.delete :default_encoding
|
|
17
|
+
|
|
18
|
+
# load template data and prepare (uses binread to avoid encoding issues)
|
|
19
|
+
data = read_template_file(file)
|
|
20
|
+
|
|
21
|
+
if data.respond_to?(:force_encoding)
|
|
22
|
+
if default_encoding
|
|
23
|
+
data = data.dup if data.frozen?
|
|
24
|
+
data.force_encoding(default_encoding)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
unless data.valid_encoding?
|
|
28
|
+
raise Encoding::InvalidByteSequenceError, "#{filename} is not valid #{data.encoding}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
data
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def digest(options)
|
|
35
|
+
options.delete_if { |_key, value| value.is_a?(Pathname) } if options.is_a?(Hash)
|
|
36
|
+
options = options.to_s unless options.is_a?(Hash)
|
|
37
|
+
if defined?(Sprockets::DigestUtils)
|
|
38
|
+
Sprockets::DigestUtils.digest(options)
|
|
39
|
+
else
|
|
40
|
+
options = options.is_a?(Hash) ? options : { value: options }
|
|
41
|
+
Digest::SHA256.hexdigest(JSON.generate(options))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def read_template_file(file)
|
|
46
|
+
data = File.open(file, 'rb', &:read)
|
|
47
|
+
if data.respond_to?(:force_encoding)
|
|
48
|
+
# Set it to the default external (without verifying)
|
|
49
|
+
data.force_encoding(Encoding.default_external) if Encoding.default_external
|
|
50
|
+
end
|
|
51
|
+
data
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def get_class_by_version(class_name, version = version_of_sprockets)
|
|
55
|
+
constantize("Sprockets::Sass::V#{version}::#{class_name}")
|
|
56
|
+
rescue
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def constantize(camel_cased_word)
|
|
61
|
+
names = camel_cased_word.split('::')
|
|
62
|
+
|
|
63
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
|
64
|
+
Object.const_get(camel_cased_word) if names.empty?
|
|
65
|
+
|
|
66
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
|
67
|
+
names.shift if names.size > 1 && names.first.empty?
|
|
68
|
+
|
|
69
|
+
names.reduce(Object) do |constant, name|
|
|
70
|
+
if constant == Object
|
|
71
|
+
constant.const_get(name)
|
|
72
|
+
else
|
|
73
|
+
candidate = constant.const_get(name)
|
|
74
|
+
next candidate if constant.const_defined?(name, false)
|
|
75
|
+
next candidate unless Object.const_defined?(name)
|
|
76
|
+
|
|
77
|
+
# Go down the ancestors to check if it is owned directly. The check
|
|
78
|
+
# stops when we reach Object or the end of ancestors tree.
|
|
79
|
+
constant = constant.ancestors.each_with_object do |ancestor, const|
|
|
80
|
+
break const if ancestor == Object
|
|
81
|
+
break ancestor if ancestor.const_defined?(name, false)
|
|
82
|
+
const
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# owner is in Object, so raise
|
|
86
|
+
constant.const_get(name, false)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def module_include(base, mod)
|
|
92
|
+
old_methods = {}
|
|
93
|
+
|
|
94
|
+
mod.instance_methods.each do |sym|
|
|
95
|
+
old_methods[sym] = base.instance_method(sym) if base.method_defined?(sym)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
mod.instance_methods.each do |sym|
|
|
99
|
+
method = mod.instance_method(sym)
|
|
100
|
+
base.send(:define_method, sym, method)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
yield
|
|
104
|
+
ensure
|
|
105
|
+
mod.instance_methods.each do |sym|
|
|
106
|
+
base.send(:undef_method, sym) if base.method_defined?(sym)
|
|
107
|
+
end
|
|
108
|
+
old_methods.each do |sym, method|
|
|
109
|
+
base.send(:define_method, sym, method)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
module Sass
|
|
4
|
+
module V2
|
|
5
|
+
# Internal: Cache wrapper for Sprockets cache adapter. (Sprockets 2.x)
|
|
6
|
+
class CacheStore < ::Sass::CacheStores::Base
|
|
7
|
+
attr_reader :environment
|
|
8
|
+
|
|
9
|
+
def initialize(environment)
|
|
10
|
+
@environment = environment
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def _store(key, version, sha, contents)
|
|
14
|
+
environment.send :cache_set, "sass/#{key}", version: version, sha: sha, contents: contents
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def _retrieve(key, version, sha)
|
|
18
|
+
obj = environment.send(:cache_get, "sass/#{key}")
|
|
19
|
+
return unless obj.is_a?(Hash)
|
|
20
|
+
return if obj[:version] != version || obj[:sha] != sha
|
|
21
|
+
obj[:obj]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
module Sass
|
|
4
|
+
module V2
|
|
5
|
+
# Class used to compress CSS files
|
|
6
|
+
class Compressor
|
|
7
|
+
def self.instance
|
|
8
|
+
@instance ||= new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.compress(input)
|
|
12
|
+
instance.compress(input)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Compresses the given CSS using Sass::Engine's
|
|
16
|
+
# :compressed output style.
|
|
17
|
+
def compress(css)
|
|
18
|
+
if css.count("\n") >= 2
|
|
19
|
+
::Sass::Engine.new(css,
|
|
20
|
+
syntax: :scss,
|
|
21
|
+
cache: false,
|
|
22
|
+
read_cache: false,
|
|
23
|
+
style: :compressed).render
|
|
24
|
+
else
|
|
25
|
+
css
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
module Sass
|
|
4
|
+
module V2
|
|
5
|
+
# Module used to inject helpers into SASS engine
|
|
6
|
+
module Functions
|
|
7
|
+
# Using Sprockets::Context#asset_data_uri return a Base64-encoded `data:`
|
|
8
|
+
# URI with the contents of the asset at the specified path.
|
|
9
|
+
#
|
|
10
|
+
# === Examples
|
|
11
|
+
#
|
|
12
|
+
# background: asset-data-uri("image.jpg"); // background: url(data:image/jpeg;base64,...);
|
|
13
|
+
#
|
|
14
|
+
def asset_data_uri(source)
|
|
15
|
+
::Sass::Script::String.new "url(#{sprockets_context.asset_data_uri(source.value)})"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Using Sprockets::Helpers#asset_path, return the full path
|
|
19
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
20
|
+
# arguments that mirror the +options+.
|
|
21
|
+
#
|
|
22
|
+
# === Examples
|
|
23
|
+
#
|
|
24
|
+
# background: url(asset-path("image.jpg")); // background: url("/assets/image.jpg");
|
|
25
|
+
# background: url(asset-path("image.jpg", $digest: true)); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
|
|
26
|
+
#
|
|
27
|
+
def asset_path(source, options = {})
|
|
28
|
+
# Work with the Sass::Rails #asset_path API
|
|
29
|
+
if options.respond_to? :value
|
|
30
|
+
kind = options.value
|
|
31
|
+
options = {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if kind && sprockets_context.respond_to?("#{kind}_path")
|
|
35
|
+
::Sass::Script::String.new sprockets_context.send("#{kind}_path", source.value), :string
|
|
36
|
+
else
|
|
37
|
+
::Sass::Script::String.new sprockets_context.asset_path(source.value, map_options(options)).to_s, :string
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Using Sprockets::Helpers#asset_path, return the url CSS
|
|
42
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
43
|
+
# arguments that mirror the +options+.
|
|
44
|
+
#
|
|
45
|
+
# === Examples
|
|
46
|
+
#
|
|
47
|
+
# background: asset-url("image.jpg"); // background: url("/assets/image.jpg");
|
|
48
|
+
# background: asset-url("image.jpg", $digest: true); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
|
|
49
|
+
#
|
|
50
|
+
def asset_url(source, options = {})
|
|
51
|
+
::Sass::Script::String.new "url(#{asset_path(source, options)})"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Using Sprockets::Helpers#image_path, return the full path
|
|
55
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
56
|
+
# arguments that mirror the +options+.
|
|
57
|
+
#
|
|
58
|
+
# === Examples
|
|
59
|
+
#
|
|
60
|
+
# background: url(image-path("image.jpg")); // background: url("/assets/image.jpg");
|
|
61
|
+
# background: url(image-path("image.jpg", $digest: true)); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
|
|
62
|
+
#
|
|
63
|
+
def image_path(source, options = {})
|
|
64
|
+
::Sass::Script::String.new sprockets_context.image_path(source.value, map_options(options)).to_s, :string
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Using Sprockets::Helpers#image_path, return the url CSS
|
|
68
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
69
|
+
# arguments that mirror the +options+.
|
|
70
|
+
#
|
|
71
|
+
# === Examples
|
|
72
|
+
#
|
|
73
|
+
# background: image-url("image.jpg"); // background: url("/assets/image.jpg");
|
|
74
|
+
# background: image-url("image.jpg", $digest: true); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
|
|
75
|
+
#
|
|
76
|
+
def image_url(source, options = {}, _cache_buster = nil)
|
|
77
|
+
# Work with the Compass #image_url API
|
|
78
|
+
if options.respond_to? :value
|
|
79
|
+
case options.value
|
|
80
|
+
when true
|
|
81
|
+
return image_path source
|
|
82
|
+
else
|
|
83
|
+
options = {}
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
::Sass::Script::String.new "url(#{image_path(source, options)})"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Using Sprockets::Helpers#font_path, return the full path
|
|
90
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
91
|
+
# arguments that mirror the +options+.
|
|
92
|
+
#
|
|
93
|
+
# === Examples
|
|
94
|
+
#
|
|
95
|
+
# src: url(font-path("font.ttf")); // src: url("/assets/font.ttf");
|
|
96
|
+
# src: url(font-path("font.ttf", $digest: true)); // src: url("/assets/font-27a8f1f96afd8d4c67a59eb9447f45bd.ttf");
|
|
97
|
+
#
|
|
98
|
+
def font_path(source, options = {})
|
|
99
|
+
::Sass::Script::String.new sprockets_context.font_path(source.value, map_options(options)).to_s, :string
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Using Sprockets::Helpers#font_path, return the url CSS
|
|
103
|
+
# for the given +source+ as a Sass String. This supports keyword
|
|
104
|
+
# arguments that mirror the +options+.
|
|
105
|
+
#
|
|
106
|
+
# === Examples
|
|
107
|
+
#
|
|
108
|
+
# src: font-url("font.ttf"); // src: url("/assets/font.ttf");
|
|
109
|
+
# src: font-url("image.jpg", $digest: true); // src: url("/assets/font-27a8f1f96afd8d4c67a59eb9447f45bd.ttf");
|
|
110
|
+
#
|
|
111
|
+
def font_url(source, options = {})
|
|
112
|
+
# Work with the Compass #font_url API
|
|
113
|
+
if options.respond_to? :value
|
|
114
|
+
case options.value
|
|
115
|
+
when true
|
|
116
|
+
return font_path source
|
|
117
|
+
else
|
|
118
|
+
options = {}
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
::Sass::Script::String.new "url(#{font_path(source, options)})"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
protected
|
|
125
|
+
|
|
126
|
+
# Returns a reference to the Sprocket's context through
|
|
127
|
+
# the importer.
|
|
128
|
+
def sprockets_context # :nodoc:
|
|
129
|
+
options[:custom][:sprockets_context]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Returns an options hash where the keys are symbolized
|
|
133
|
+
# and the values are unwrapped Sass literals.
|
|
134
|
+
def map_options(options = {}) # :nodoc:
|
|
135
|
+
::Sass::Util.map_hash(options) do |key, value|
|
|
136
|
+
[key.to_sym, value.respond_to?(:value) ? value.value : value]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sprockets
|
|
4
|
+
module Sass
|
|
5
|
+
module V2
|
|
6
|
+
# class used for importing files from SCCS and SASS files
|
|
7
|
+
class Importer < ::Sass::Importers::Base
|
|
8
|
+
GLOB = /\*|\[.+\]/
|
|
9
|
+
|
|
10
|
+
# @see Sass::Importers::Base#find_relative
|
|
11
|
+
def find_relative(path, base_path, options)
|
|
12
|
+
if path.to_s =~ GLOB
|
|
13
|
+
engine_from_glob(path, base_path, options)
|
|
14
|
+
else
|
|
15
|
+
engine_from_path(path, base_path, options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @see Sass::Importers::Base#find
|
|
20
|
+
def find(path, options)
|
|
21
|
+
engine_from_path(path, nil, options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @see Sass::Importers::Base#mtime
|
|
25
|
+
def mtime(path, _options)
|
|
26
|
+
if pathname = resolve(path)
|
|
27
|
+
pathname.mtime
|
|
28
|
+
end
|
|
29
|
+
rescue Errno::ENOENT
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @see Sass::Importers::Base#key
|
|
34
|
+
def key(path, _options)
|
|
35
|
+
path = Pathname.new(path)
|
|
36
|
+
["#{self.class.name}:#{path.dirname.expand_path}", path.basename]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @see Sass::Importers::Base#to_s
|
|
40
|
+
def to_s
|
|
41
|
+
inspect
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
protected
|
|
45
|
+
|
|
46
|
+
# Create a Sass::Engine from the given path.
|
|
47
|
+
def engine_from_path(path, base_path, options)
|
|
48
|
+
context = options[:custom][:sprockets_context]
|
|
49
|
+
(pathname = resolve(context, path, base_path)) || (return nil)
|
|
50
|
+
context.depend_on pathname
|
|
51
|
+
::Sass::Engine.new evaluate(context, pathname), options.merge(
|
|
52
|
+
filename: pathname.to_s,
|
|
53
|
+
syntax: syntax(pathname),
|
|
54
|
+
importer: self,
|
|
55
|
+
custom: { sprockets_context: context }
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Create a Sass::Engine that will handle importing
|
|
60
|
+
# a glob of files.
|
|
61
|
+
def engine_from_glob(glob, base_path, options)
|
|
62
|
+
context = options[:custom][:sprockets_context]
|
|
63
|
+
engine_imports = resolve_glob(context, glob, base_path).reduce(''.dup) do |imports, path|
|
|
64
|
+
context.depend_on path
|
|
65
|
+
relative_path = path.relative_path_from Pathname.new(base_path).dirname
|
|
66
|
+
imports << %(@import "#{relative_path}";\n)
|
|
67
|
+
end
|
|
68
|
+
return nil if engine_imports.empty?
|
|
69
|
+
::Sass::Engine.new engine_imports, options.merge(
|
|
70
|
+
filename: base_path.to_s,
|
|
71
|
+
syntax: syntax(base_path.to_s),
|
|
72
|
+
importer: self,
|
|
73
|
+
custom: { sprockets_context: context }
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Finds an asset from the given path. This is where
|
|
78
|
+
# we make Sprockets behave like Sass, and import partial
|
|
79
|
+
# style paths.
|
|
80
|
+
def resolve(context, path, base_path)
|
|
81
|
+
paths, _root_path = possible_files(context, path, base_path)
|
|
82
|
+
paths.each do |file|
|
|
83
|
+
context.resolve(file.to_s) do |found|
|
|
84
|
+
return found if context.asset_requirable?(found)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Finds all of the assets using the given glob.
|
|
91
|
+
def resolve_glob(context, glob, base_path)
|
|
92
|
+
base_path = Pathname.new(base_path)
|
|
93
|
+
path_with_glob = base_path.dirname.join(glob).to_s
|
|
94
|
+
|
|
95
|
+
Pathname.glob(path_with_glob).sort.select do |path|
|
|
96
|
+
asset_requirable = context.asset_requirable?(path)
|
|
97
|
+
path != context.pathname && asset_requirable
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def context_root_path(context)
|
|
102
|
+
Pathname.new(context.root_path)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def context_load_pathnames(context)
|
|
106
|
+
context.environment.paths.map { |p| Pathname.new(p) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns all of the possible paths (including partial variations)
|
|
110
|
+
# to attempt to resolve with the given path.
|
|
111
|
+
def possible_files(context, path, base_path)
|
|
112
|
+
path = Pathname.new(path)
|
|
113
|
+
base_path = Pathname.new(base_path).dirname
|
|
114
|
+
partial_path = partialize_path(path)
|
|
115
|
+
additional_paths = [Pathname.new("#{path}.css"), Pathname.new("#{partial_path}.css"), Pathname.new("#{path}.css.#{syntax(path)}"), Pathname.new("#{partial_path}.css.#{syntax(path)}")]
|
|
116
|
+
paths = additional_paths.concat([path, partial_path])
|
|
117
|
+
|
|
118
|
+
# Find base_path's root
|
|
119
|
+
paths, root_path = add_root_to_possible_files(context, base_path, path, paths)
|
|
120
|
+
[paths.compact, root_path]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def add_root_to_possible_files(context, base_path, path, paths)
|
|
124
|
+
env_root_paths = context_load_pathnames(context)
|
|
125
|
+
root_path = env_root_paths.find do |env_root_path|
|
|
126
|
+
base_path.to_s.start_with?(env_root_path.to_s)
|
|
127
|
+
end
|
|
128
|
+
root_path ||= context_root_path(context)
|
|
129
|
+
# Add the relative path from the root, if necessary
|
|
130
|
+
if path.relative? && base_path != root_path
|
|
131
|
+
relative_path = base_path.relative_path_from(root_path).join path
|
|
132
|
+
paths.unshift(relative_path, partialize_path(relative_path))
|
|
133
|
+
end
|
|
134
|
+
[paths, root_path]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Returns the partialized version of the given path.
|
|
138
|
+
# Returns nil if the path is already to a partial.
|
|
139
|
+
def partialize_path(path)
|
|
140
|
+
return unless path.basename.to_s !~ /\A_/
|
|
141
|
+
Pathname.new path.to_s.sub(/([^\/]+)\Z/, '_\1')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Returns the Sass syntax of the given path.
|
|
145
|
+
def syntax(path)
|
|
146
|
+
path.to_s.include?('.sass') ? :sass : :scss
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def syntax_mime_type(_path)
|
|
150
|
+
'text/css'
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def filtered_processor_classes
|
|
154
|
+
classes = [Sprockets::Sass::Utils.get_class_by_version('SassTemplate'), Sprockets::Sass::Utils.get_class_by_version('ScssTemplate')]
|
|
155
|
+
classes << Sprockets::SassProcessor if defined?(Sprockets::SassProcessor)
|
|
156
|
+
classes << Sprockets::SasscProcessor if defined?(Sprockets::SasscProcessor)
|
|
157
|
+
classes
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def content_type_of_path(context, path)
|
|
161
|
+
attributes = context.environment.attributes_for(path)
|
|
162
|
+
content_type = attributes.content_type
|
|
163
|
+
[content_type, attributes]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def get_context_preprocessors(context, content_type)
|
|
167
|
+
context.environment.preprocessors(content_type)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def get_context_transformers(_context, _content_type, _path)
|
|
171
|
+
[]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def get_engines_from_attributes(context, attributes)
|
|
175
|
+
attributes.engines
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def get_all_processors_for_evaluate(context, content_type, attributes, path)
|
|
179
|
+
engines = get_engines_from_attributes(context, attributes)
|
|
180
|
+
preprocessors = get_context_preprocessors(context, content_type)
|
|
181
|
+
additional_transformers = get_context_transformers(context, content_type, path)
|
|
182
|
+
additional_transformers.reverse + preprocessors + engines.reverse
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def filter_all_processors(processors)
|
|
186
|
+
processors.delete_if do |processor|
|
|
187
|
+
filtered_processor_classes.include?(processor) || filtered_processor_classes.any? do |filtered_processor|
|
|
188
|
+
!processor.is_a?(Proc) && processor < filtered_processor
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def evaluate_path_from_context(context, path, processors)
|
|
194
|
+
context.evaluate(path, processors: processors)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Returns the string to be passed to the Sass engine. We use
|
|
198
|
+
# Sprockets to process the file, but we remove any Sass processors
|
|
199
|
+
# because we need to let the Sass::Engine handle that.
|
|
200
|
+
def evaluate(context, path)
|
|
201
|
+
content_type, attributes = content_type_of_path(context, path)
|
|
202
|
+
processors = get_all_processors_for_evaluate(context, content_type, attributes, path)
|
|
203
|
+
filter_all_processors(processors)
|
|
204
|
+
evaluate_path_from_context(context, path, processors)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|