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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.reek +12 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +77 -0
  5. data/.travis.yml +14 -0
  6. data/Appraisals +32 -15
  7. data/Gemfile +5 -0
  8. data/README.md +41 -4
  9. data/Rakefile +15 -3
  10. data/gemfiles/compass_0.11.gemfile +1 -1
  11. data/gemfiles/sass_3.1.gemfile +1 -1
  12. data/gemfiles/sass_3.3.gemfile +8 -0
  13. data/gemfiles/sass_3.4.gemfile +8 -0
  14. data/gemfiles/sprockets_2.10.gemfile +1 -1
  15. data/gemfiles/sprockets_2.11.gemfile +1 -1
  16. data/gemfiles/sprockets_2.2.gemfile +1 -1
  17. data/gemfiles/sprockets_2.3.gemfile +1 -1
  18. data/gemfiles/sprockets_2.4.gemfile +1 -1
  19. data/gemfiles/sprockets_2.5.gemfile +1 -1
  20. data/gemfiles/sprockets_2.6.gemfile +1 -1
  21. data/gemfiles/sprockets_2.7.gemfile +1 -1
  22. data/gemfiles/sprockets_2.8.gemfile +1 -1
  23. data/gemfiles/sprockets_2.9.gemfile +1 -1
  24. data/gemfiles/sprockets_3.7.gemfile +9 -0
  25. data/gemfiles/sprockets_4.0.gemfile +10 -0
  26. data/gemfiles/sprockets_4.0_beta2.gemfile +9 -0
  27. data/lib/sprockets/sass/functions.rb +24 -159
  28. data/lib/sprockets/sass/registration.rb +126 -0
  29. data/lib/sprockets/sass/utils.rb +115 -0
  30. data/lib/sprockets/sass/v2/cache_store.rb +26 -0
  31. data/lib/sprockets/sass/v2/compressor.rb +31 -0
  32. data/lib/sprockets/sass/v2/functions.rb +142 -0
  33. data/lib/sprockets/sass/v2/importer.rb +209 -0
  34. data/lib/sprockets/sass/v2/sass_template.rb +221 -0
  35. data/lib/sprockets/sass/v2/scss_template.rb +14 -0
  36. data/lib/sprockets/sass/v3/cache_store.rb +28 -0
  37. data/lib/sprockets/sass/v3/compressor.rb +97 -0
  38. data/lib/sprockets/sass/v3/functions.rb +12 -0
  39. data/lib/sprockets/sass/v3/importer.rb +224 -0
  40. data/lib/sprockets/sass/v3/sass_template.rb +37 -0
  41. data/lib/sprockets/sass/v3/scss_template.rb +15 -0
  42. data/lib/sprockets/sass/v4/cache_store.rb +11 -0
  43. data/lib/sprockets/sass/v4/compressor.rb +11 -0
  44. data/lib/sprockets/sass/v4/functions.rb +12 -0
  45. data/lib/sprockets/sass/v4/importer.rb +109 -0
  46. data/lib/sprockets/sass/v4/sass_template.rb +27 -0
  47. data/lib/sprockets/sass/v4/scss_template.rb +16 -0
  48. data/lib/sprockets/sass/version.rb +2 -1
  49. data/lib/sprockets/sass.rb +37 -12
  50. data/lib/sprockets-sass.rb +1 -0
  51. data/spec/custom_importer_spec.rb +4 -6
  52. data/spec/importer_spec.rb +44 -0
  53. data/spec/spec_helper.rb +39 -3
  54. data/spec/sprockets-sass_spec.rb +102 -62
  55. data/spec/support/be_fresh_matcher.rb +12 -8
  56. data/spec/support/dummy_importer.rb +1 -1
  57. data/spec/support/fail_postprocessor.rb +23 -0
  58. data/spec/support/fake_engine.rb +50 -0
  59. data/spec/support/sass_template.rb +11 -0
  60. data/spec/support/test_registration.rb +11 -0
  61. data/sprockets-sass.gemspec +28 -9
  62. metadata +100 -97
  63. data/gemfiles/compass_0.12.gemfile +0 -7
  64. data/gemfiles/sass_3.2.gemfile +0 -7
  65. data/gemfiles/sprockets_2.0.gemfile +0 -7
  66. data/gemfiles/sprockets_2.1.gemfile +0 -9
  67. data/lib/sprockets/sass/cache_store.rb +0 -27
  68. data/lib/sprockets/sass/compressor.rb +0 -22
  69. data/lib/sprockets/sass/importer.rb +0 -142
  70. data/lib/sprockets/sass/sass_template.rb +0 -115
  71. 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