sprockets-sass 1.3.1 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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