sprockets-sass 1.3.1 → 2.0.0.beta1

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.reek +12 -0
  3. data/.rubocop.yml +77 -0
  4. data/.travis.yml +16 -0
  5. data/Appraisals +32 -15
  6. data/Gemfile +5 -0
  7. data/README.md +41 -4
  8. data/Rakefile +15 -3
  9. data/gemfiles/compass_0.11.gemfile +1 -1
  10. data/gemfiles/sass_3.1.gemfile +1 -1
  11. data/gemfiles/sass_3.3.gemfile +8 -0
  12. data/gemfiles/sass_3.4.gemfile +8 -0
  13. data/gemfiles/sprockets_2.10.gemfile +1 -1
  14. data/gemfiles/sprockets_2.11.gemfile +1 -1
  15. data/gemfiles/sprockets_2.2.gemfile +1 -1
  16. data/gemfiles/sprockets_2.3.gemfile +1 -1
  17. data/gemfiles/sprockets_2.4.gemfile +1 -1
  18. data/gemfiles/sprockets_2.5.gemfile +1 -1
  19. data/gemfiles/sprockets_2.6.gemfile +1 -1
  20. data/gemfiles/sprockets_2.7.gemfile +1 -1
  21. data/gemfiles/sprockets_2.8.gemfile +1 -1
  22. data/gemfiles/sprockets_2.9.gemfile +1 -1
  23. data/gemfiles/sprockets_3.7.gemfile +9 -0
  24. data/gemfiles/sprockets_4.0.gemfile +10 -0
  25. data/gemfiles/sprockets_4.0_beta2.gemfile +9 -0
  26. data/lib/sprockets-sass.rb +1 -0
  27. data/lib/sprockets/sass.rb +37 -12
  28. data/lib/sprockets/sass/functions.rb +24 -159
  29. data/lib/sprockets/sass/registration.rb +126 -0
  30. data/lib/sprockets/sass/utils.rb +115 -0
  31. data/lib/sprockets/sass/v2/cache_store.rb +26 -0
  32. data/lib/sprockets/sass/v2/compressor.rb +31 -0
  33. data/lib/sprockets/sass/v2/functions.rb +142 -0
  34. data/lib/sprockets/sass/v2/importer.rb +209 -0
  35. data/lib/sprockets/sass/v2/sass_template.rb +221 -0
  36. data/lib/sprockets/sass/v2/scss_template.rb +14 -0
  37. data/lib/sprockets/sass/v3/cache_store.rb +28 -0
  38. data/lib/sprockets/sass/v3/compressor.rb +97 -0
  39. data/lib/sprockets/sass/v3/functions.rb +12 -0
  40. data/lib/sprockets/sass/v3/importer.rb +212 -0
  41. data/lib/sprockets/sass/v3/sass_template.rb +37 -0
  42. data/lib/sprockets/sass/v3/scss_template.rb +15 -0
  43. data/lib/sprockets/sass/v4/cache_store.rb +11 -0
  44. data/lib/sprockets/sass/v4/compressor.rb +11 -0
  45. data/lib/sprockets/sass/v4/functions.rb +12 -0
  46. data/lib/sprockets/sass/v4/importer.rb +105 -0
  47. data/lib/sprockets/sass/v4/sass_template.rb +27 -0
  48. data/lib/sprockets/sass/v4/scss_template.rb +16 -0
  49. data/lib/sprockets/sass/version.rb +2 -1
  50. data/spec/custom_importer_spec.rb +4 -6
  51. data/spec/spec_helper.rb +30 -3
  52. data/spec/sprockets-sass_spec.rb +101 -61
  53. data/spec/support/be_fresh_matcher.rb +10 -6
  54. data/spec/support/dummy_importer.rb +1 -1
  55. data/spec/support/fail_postprocessor.rb +23 -0
  56. data/spec/support/sass_template.rb +11 -0
  57. data/sprockets-sass.gemspec +27 -8
  58. metadata +92 -95
  59. data/gemfiles/compass_0.12.gemfile +0 -7
  60. data/gemfiles/sass_3.2.gemfile +0 -7
  61. data/gemfiles/sprockets_2.0.gemfile +0 -7
  62. data/gemfiles/sprockets_2.1.gemfile +0 -9
  63. data/lib/sprockets/sass/cache_store.rb +0 -27
  64. data/lib/sprockets/sass/compressor.rb +0 -22
  65. data/lib/sprockets/sass/importer.rb +0 -142
  66. data/lib/sprockets/sass/sass_template.rb +0 -115
  67. data/lib/sprockets/sass/scss_template.rb +0 -12
@@ -1,163 +1,28 @@
1
- require 'sass'
2
-
3
- module Sprockets
4
- module Sass
1
+ # frozen_string_literal: true
2
+ module Sass
3
+ module Script
4
+ # Original Sass script functions are overidden with this methods
5
5
  module Functions
6
- # Using Sprockets::Context#asset_data_uri return a Base64-encoded `data:`
7
- # URI with the contents of the asset at the specified path.
8
- #
9
- # === Examples
10
- #
11
- # background: asset-data-uri("image.jpg"); // background: url(data:image/jpeg;base64,...);
12
- #
13
- def asset_data_uri(source)
14
- ::Sass::Script::String.new "url(#{sprockets_context.asset_data_uri(source.value)})"
15
- end
16
-
17
- # Using Sprockets::Helpers#asset_path, return the full path
18
- # for the given +source+ as a Sass String. This supports keyword
19
- # arguments that mirror the +options+.
20
- #
21
- # === Examples
22
- #
23
- # background: url(asset-path("image.jpg")); // background: url("/assets/image.jpg");
24
- # background: url(asset-path("image.jpg", $digest: true)); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
25
- #
26
- def asset_path(source, options = {})
27
- # Work with the Sass::Rails #asset_path API
28
- if options.respond_to? :value
29
- kind = options.value
30
- options = {}
31
- end
32
-
33
- if kind && sprockets_context.respond_to?("#{kind}_path")
34
- ::Sass::Script::String.new sprockets_context.send("#{kind}_path", source.value), :string
35
- else
36
- ::Sass::Script::String.new sprockets_context.asset_path(source.value, map_options(options)).to_s, :string
37
- end
38
- end
39
-
40
- # Using Sprockets::Helpers#asset_path, return the url CSS
41
- # for the given +source+ as a Sass String. This supports keyword
42
- # arguments that mirror the +options+.
43
- #
44
- # === Examples
45
- #
46
- # background: asset-url("image.jpg"); // background: url("/assets/image.jpg");
47
- # background: asset-url("image.jpg", $digest: true); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
48
- #
49
- def asset_url(source, options = {})
50
- ::Sass::Script::String.new "url(#{asset_path(source, options)})"
51
- end
52
-
53
- # Using Sprockets::Helpers#image_path, return the full path
54
- # for the given +source+ as a Sass String. This supports keyword
55
- # arguments that mirror the +options+.
56
- #
57
- # === Examples
58
- #
59
- # background: url(image-path("image.jpg")); // background: url("/assets/image.jpg");
60
- # background: url(image-path("image.jpg", $digest: true)); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
61
- #
62
- def image_path(source, options = {})
63
- ::Sass::Script::String.new sprockets_context.image_path(source.value, map_options(options)).to_s, :string
64
- end
65
-
66
- # Using Sprockets::Helpers#image_path, return the url CSS
67
- # for the given +source+ as a Sass String. This supports keyword
68
- # arguments that mirror the +options+.
69
- #
70
- # === Examples
71
- #
72
- # background: image-url("image.jpg"); // background: url("/assets/image.jpg");
73
- # background: image-url("image.jpg", $digest: true); // background: url("/assets/image-27a8f1f96afd8d4c67a59eb9447f45bd.jpg");
74
- #
75
- def image_url(source, options = {}, cache_buster = nil)
76
- # Work with the Compass #image_url API
77
- if options.respond_to? :value
78
- case options.value
79
- when true
80
- return image_path source
81
- else
82
- options = {}
83
- end
84
- end
85
- ::Sass::Script::String.new "url(#{image_path(source, options)})"
86
- end
87
-
88
- # Using Sprockets::Helpers#font_path, return the full path
89
- # for the given +source+ as a Sass String. This supports keyword
90
- # arguments that mirror the +options+.
91
- #
92
- # === Examples
93
- #
94
- # src: url(font-path("font.ttf")); // src: url("/assets/font.ttf");
95
- # src: url(font-path("font.ttf", $digest: true)); // src: url("/assets/font-27a8f1f96afd8d4c67a59eb9447f45bd.ttf");
96
- #
97
- def font_path(source, options = {})
98
- ::Sass::Script::String.new sprockets_context.font_path(source.value, map_options(options)).to_s, :string
99
- end
100
-
101
- # Using Sprockets::Helpers#font_path, return the url CSS
102
- # for the given +source+ as a Sass String. This supports keyword
103
- # arguments that mirror the +options+.
104
- #
105
- # === Examples
106
- #
107
- # src: font-url("font.ttf"); // src: url("/assets/font.ttf");
108
- # src: font-url("image.jpg", $digest: true); // src: url("/assets/font-27a8f1f96afd8d4c67a59eb9447f45bd.ttf");
109
- #
110
- def font_url(source, options = {})
111
- # Work with the Compass #font_url API
112
- if options.respond_to? :value
113
- case options.value
114
- when true
115
- return font_path source
116
- else
117
- options = {}
118
- end
119
- end
120
- ::Sass::Script::String.new "url(#{font_path(source, options)})"
121
- end
122
-
123
- protected
124
-
125
- # Returns a reference to the Sprocket's context through
126
- # the importer.
127
- def sprockets_context # :nodoc:
128
- options[:custom][:sprockets_context]
129
- end
130
-
131
- # Returns an options hash where the keys are symbolized
132
- # and the values are unwrapped Sass literals.
133
- def map_options(options = {}) # :nodoc:
134
- ::Sass::Util.map_hash(options) do |key, value|
135
- [key.to_sym, value.respond_to?(:value) ? value.value : value]
136
- end
137
- end
6
+ include Sprockets::Sass::Utils.get_class_by_version('Functions')
7
+
8
+ # Hack to ensure previous API declarations (by Compass or whatever)
9
+ # don't take precedence.
10
+ %i(asset_path asset_url image_path image_url font_path font_url asset_data_uri).each do |method|
11
+ defined?(@signatures) && @signatures.delete(method)
12
+ end
13
+
14
+ declare :asset_path, [:source], var_kwargs: true
15
+ declare :asset_path, %i(source kind)
16
+ declare :asset_url, [:source], var_kwargs: true
17
+ declare :asset_url, %i(source kind)
18
+ declare :image_path, [:source], var_kwargs: true
19
+ declare :image_url, [:source], var_kwargs: true
20
+ declare :image_url, %i(source only_path)
21
+ declare :image_url, %i(source only_path cache_buster)
22
+ declare :font_path, [:source], var_kwargs: true
23
+ declare :font_url, [:source], var_kwargs: true
24
+ declare :font_url, %i(source only_path)
25
+ declare :asset_data_uri, [:source]
138
26
  end
139
27
  end
140
28
  end
141
-
142
- module Sass::Script::Functions
143
- include Sprockets::Sass::Functions
144
-
145
- # Hack to ensure previous API declarations (by Compass or whatever)
146
- # don't take precedence.
147
- [:asset_path, :asset_url, :image_path, :image_url, :font_path, :font_url, :asset_data_uri].each do |method|
148
- defined?(@signatures) && @signatures.delete(method)
149
- end
150
-
151
- declare :asset_path, [:source], :var_kwargs => true
152
- declare :asset_path, [:source, :kind]
153
- declare :asset_url, [:source], :var_kwargs => true
154
- declare :asset_url, [:source, :kind]
155
- declare :image_path, [:source], :var_kwargs => true
156
- declare :image_url, [:source], :var_kwargs => true
157
- declare :image_url, [:source, :only_path]
158
- declare :image_url, [:source, :only_path, :cache_buster]
159
- declare :font_path, [:source], :var_kwargs => true
160
- declare :font_url, [:source], :var_kwargs => true
161
- declare :font_url, [:source, :only_path]
162
- declare :asset_data_uri, [:source]
163
- end
@@ -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