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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ module Sass
4
+ module V2
5
+ # Preprocessor for SCSS files
6
+ class ScssTemplate < Sprockets::Sass::V2::SassTemplate
7
+ # Define the expected syntax for the template
8
+ def self.syntax
9
+ :scss
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ module Sprockets
3
+ module Sass
4
+ module V3
5
+ # Internal: Cache wrapper for Sprockets cache adapter. (Sprockets >= 3)
6
+ class CacheStore < ::Sass::CacheStores::Base
7
+ VERSION = '1'
8
+
9
+ def initialize(cache, version)
10
+ @cache = cache
11
+ @version = "#{VERSION}/#{version}"
12
+ end
13
+
14
+ def _store(key, version, sha, contents)
15
+ @cache.set("#{@version}/#{version}/#{key}/#{sha}", contents, true)
16
+ end
17
+
18
+ def _retrieve(key, version, sha)
19
+ @cache.get("#{@version}/#{version}/#{key}/#{sha}", true)
20
+ end
21
+
22
+ def path_to(key)
23
+ key
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sprockets
4
+ module Sass
5
+ module V3
6
+ # Class used to compress CSS files
7
+ class Compressor
8
+ VERSION = '1'
9
+
10
+ def self.default_mime_type
11
+ 'text/css'
12
+ end
13
+
14
+ # Public: Return singleton instance with default options.
15
+ #
16
+ # Returns SassCompressor object.
17
+ def self.instance
18
+ @instance ||= new
19
+ end
20
+
21
+ def self.call(input)
22
+ instance.call(input)
23
+ end
24
+
25
+ def self.cache_key
26
+ instance.cache_key
27
+ end
28
+
29
+ attr_reader :cache_key, :input, :filename, :source, :options, :context
30
+
31
+ def initialize(options = {})
32
+ @default_options = {
33
+ syntax: :scss,
34
+ cache: false,
35
+ read_cache: false,
36
+ style: :compressed,
37
+ default_encoding: Encoding.default_external || 'utf-8'
38
+ }
39
+ @options = @default_options
40
+ @cache_key = "#{self.class.name}:#{::Sass::VERSION}:#{VERSION}:#{Sprockets::Sass::Utils.digest(options)}"
41
+ if options.is_a?(Hash)
42
+ @input = options
43
+ @filename = options[:filename]
44
+ @source = options[:data]
45
+ @options = @options.merge(options)
46
+ else
47
+ @filename = options
48
+ @source = block_given? ? yield : nil
49
+ end
50
+ end
51
+
52
+ def call(input)
53
+ @input = input
54
+ if input.is_a?(Hash)
55
+ @filename = input[:filename]
56
+ @source = input[:data]
57
+ @context = input[:environment].context_class.new(input)
58
+ end
59
+ data = filename || @input
60
+ run(data)
61
+ end
62
+
63
+ def render(context, _empty_hash_wtf)
64
+ @context = context
65
+ run(filename)
66
+ end
67
+
68
+ def self.compress(input)
69
+ call(input)
70
+ end
71
+
72
+ def run(string)
73
+ data = File.exist?(string.to_s) ? Sprockets::Sass::Utils.read_file_binary(string, options) : string
74
+ if data.count("\n") >= 2
75
+ engine = ::Sass::Engine.new(data, @options.merge(filename: filename))
76
+ css = engine.render
77
+ # this is defined when using sprockets environment,
78
+ # but is not defined when calling directly in tests
79
+ # unless using the manifest to compile the assets
80
+ if defined?(@context) && @context.respond_to?(:metadata)
81
+ sass_dependencies = Set.new([filename])
82
+ engine.dependencies.map do |dependency|
83
+ sass_dependencies << dependency.options[:filename]
84
+ context.metadata[:dependencies] << Sprockets::URIUtils.build_file_digest_uri(dependency.options[:filename])
85
+ end
86
+ context.metadata.merge(data: css, sass_dependencies: sass_dependencies)
87
+ else
88
+ css
89
+ end
90
+ else
91
+ data
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../v2/functions'
3
+ module Sprockets
4
+ module Sass
5
+ module V3
6
+ # Module used to inject helpers into SASS engine
7
+ module Functions
8
+ include Sprockets::Sass::V2::Functions
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../v2/importer'
3
+ module Sprockets
4
+ module Sass
5
+ module V3
6
+ # class used for importing files from SCCS and SASS files
7
+ class Importer < Sprockets::Sass::V2::Importer
8
+ GLOB = /\*|\[.+\]/
9
+
10
+ protected
11
+
12
+ def resolve_path_with_load_paths(context, path, root_path, file)
13
+ context.resolve(file.to_s, load_paths: context.environment.paths, base_path: root_path, accept: syntax_mime_type(path))
14
+ rescue
15
+ nil
16
+ end
17
+
18
+ # Finds an asset from the given path. This is where
19
+ # we make Sprockets behave like Sass, and import partial
20
+ # style paths.
21
+ def resolve(context, path, base_path)
22
+ paths, root_path = possible_files(context, path, base_path)
23
+ paths.each do |file|
24
+ found_item = resolve_path_with_load_paths(context, path, root_path, file)
25
+ return found_item if !found_item.nil? && asset_requirable?(context, found_item)
26
+ end
27
+ nil
28
+ end
29
+
30
+ def stat_of_pathname(context, _pathname, path)
31
+ context.environment.stat(path)
32
+ end
33
+
34
+ def available_content_types(path)
35
+ ['text/css', syntax_mime_type(path), "text/#{syntax(path)}+ruby"].compact.uniq
36
+ end
37
+
38
+ def check_context_content_types(context, path)
39
+ available_mimes = available_content_types(path)
40
+ path_content_type, _attributes = content_type_of_path(context, path)
41
+ context.content_type.nil? || available_mimes.include?(path_content_type) || available_mimes.include?(context.content_type)
42
+ end
43
+
44
+ def asset_requirable?(context, path)
45
+ pathname = begin
46
+ context.resolve(path)
47
+ rescue
48
+ nil
49
+ end
50
+ return false if pathname.nil?
51
+ stat = stat_of_pathname(context, pathname, path)
52
+ return false unless stat && stat.file?
53
+ check_context_content_types(context, path)
54
+ end
55
+
56
+ # Finds all of the assets using the given glob.
57
+ def resolve_glob(context, glob, base_path)
58
+ base_path = Pathname.new(base_path)
59
+ path_with_glob = base_path.dirname.join(glob).to_s
60
+
61
+ Pathname.glob(path_with_glob).sort.select do |path|
62
+ asset_requirable = asset_requirable?(context, path)
63
+ path != context.pathname && asset_requirable
64
+ end
65
+ end
66
+
67
+ # Returns all of the possible paths (including partial variations)
68
+ # to attempt to resolve with the given path.
69
+ def possible_files(context, path, base_path)
70
+ paths, root_path = super(context, path, base_path)
71
+ paths = additional_paths_for_sprockets(context, paths, path, base_path)
72
+ [paths.compact, root_path]
73
+ end
74
+
75
+ def additional_paths_for_sprockets(context, paths, path, base_path)
76
+ relatives = paths.map { |path_detected| path_detected.to_s.start_with?('.') ? Pathname.new(path_detected) : Pathname.new(path_detected.to_s.prepend('./')) }
77
+ context.environment.paths.each do |load_path|
78
+ relative_path = Pathname.new(base_path).relative_path_from(Pathname.new(load_path)).join(path)
79
+ paths.unshift(relative_path, partialize_path(relative_path))
80
+ end
81
+ paths = paths.unshift(relatives)
82
+ paths
83
+ end
84
+
85
+ def filtered_processor_classes
86
+ classes = super
87
+ classes << Sprockets::Preprocessors::DefaultSourceMap if defined?(Sprockets::Preprocessors::DefaultSourceMap)
88
+ classes << Sprockets::SourceMapProcessor if defined?(Sprockets::SourceMapProcessor)
89
+ classes
90
+ end
91
+
92
+ def content_type_of_path(context, path)
93
+ attributes = context.environment.send(:parse_path_extnames, path.to_s)
94
+ content_type = attributes[1]
95
+ [content_type, attributes]
96
+ end
97
+
98
+ def build_input_for_process(context, path, data)
99
+ content_type, _attributes = content_type_of_path(context, path)
100
+ {
101
+ environment: context.environment,
102
+ cache: context.environment.cache,
103
+ uri: path.to_s,
104
+ filename: path.to_s,
105
+ load_path: context.environment.paths,
106
+ name: File.basename(path),
107
+ content_type: content_type,
108
+ data: data,
109
+ metadata: context.metadata
110
+ }
111
+ end
112
+
113
+ def check_path_before_process(_context, path)
114
+ path
115
+ end
116
+
117
+ def call_processor_input(processor, context, input, processors)
118
+ metadata = (input[:metadata] || {}).dup
119
+ metadata[:data] = input[:data]
120
+ result = processor.call(input)
121
+ handle_process_result(context, result, processors, metadata)
122
+ end
123
+
124
+ def handle_complex_process_result(context, result, processors)
125
+ data = result[:data] if result.key?(:data)
126
+ context.metadata.merge!(result)
127
+ context.metadata.delete(:data)
128
+ if result.key?(:required)
129
+ result[:required].each do |file|
130
+ file_asset = context.environment.load(file)
131
+ data += process(processors, context, file_asset.filename)
132
+ end
133
+ end
134
+ data
135
+ end
136
+
137
+ def handle_process_result(context, result, processors, metadata)
138
+ data = nil
139
+ case result
140
+ when NilClass
141
+ data = metadata[:data]
142
+ when Hash
143
+ data = handle_complex_process_result(context, result, processors)
144
+ when String
145
+ data = result
146
+ else
147
+ raise Error, "invalid processor return type: #{result.class}"
148
+ end
149
+ data
150
+ end
151
+
152
+ # Internal: Run processors on filename and data.
153
+ #
154
+ # Returns Hash.
155
+ def process(processors, context, path)
156
+ path = check_path_before_process(context, path)
157
+ data = Sprockets::Sass::Utils.read_template_file(path.to_s)
158
+ input = build_input_for_process(context, path, data)
159
+
160
+ processors.each do |processor|
161
+ data = call_processor_input(processor, context, input, processors)
162
+ end
163
+
164
+ data
165
+ end
166
+
167
+ def get_context_preprocessors(context, content_type)
168
+ context.environment.preprocessors[content_type].map { |a| a.class == Class ? a : a.class }
169
+ end
170
+
171
+ def get_context_transformers(context, content_type, path)
172
+ available_transformers = context.environment.transformers[content_type]
173
+ additional_transformers = available_transformers.key?(syntax_mime_type(path)) ? available_transformers[syntax_mime_type(path)] : []
174
+ additional_transformers.is_a?(Array) ? additional_transformers : [additional_transformers]
175
+ end
176
+
177
+ def get_engines_from_attributes(_attributes)
178
+ []
179
+ end
180
+
181
+ def get_all_processors_for_evaluate(context, content_type, attributes, path)
182
+ engines = get_engines_from_attributes(attributes)
183
+ preprocessors = get_context_preprocessors(context, content_type)
184
+ additional_transformers = get_context_transformers(context, content_type, path)
185
+ additional_transformers.reverse + preprocessors + engines.reverse
186
+ end
187
+
188
+ def filter_all_processors(processors)
189
+ processors.delete_if do |processor|
190
+ filtered_processor_classes.include?(processor) || filtered_processor_classes.any? do |filtered_processor|
191
+ !processor.is_a?(Proc) && processor < filtered_processor
192
+ end
193
+ end
194
+ end
195
+
196
+ def evaluate_path_from_context(context, path, processors)
197
+ process(processors, context, path)
198
+ end
199
+
200
+ # Returns the string to be passed to the Sass engine. We use
201
+ # Sprockets to process the file, but we remove any Sass processors
202
+ # because we need to let the Sass::Engine handle that.
203
+ def evaluate(context, path)
204
+ content_type, attributes = content_type_of_path(context, path)
205
+ processors = get_all_processors_for_evaluate(context, content_type, attributes, path)
206
+ filter_all_processors(processors)
207
+ evaluate_path_from_context(context, path, processors)
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../v2/sass_template'
3
+ module Sprockets
4
+ module Sass
5
+ module V3
6
+ # Preprocessor for SASS files
7
+ class SassTemplate < Sprockets::Sass::V2::SassTemplate
8
+ def build_cache_store(context)
9
+ return nil if context.environment.cache.nil?
10
+ cache = @input[:cache]
11
+ version = @cache_version
12
+ if defined?(Sprockets::SassProcessor::CacheStore)
13
+ Sprockets::SassProcessor::CacheStore.new(cache, version)
14
+ else
15
+ custom_cache_store(cache, version)
16
+ end
17
+ end
18
+
19
+ def custom_cache_store(*args)
20
+ Sprockets::Sass::V3::CacheStore.new(*args)
21
+ end
22
+
23
+ # Allow the use of custom SASS importers, making sure the
24
+ # custom importer is a `Sprockets::Sass::Importer`
25
+ def fetch_importer_class
26
+ if defined?(@importer_class) && !@importer_class.nil?
27
+ @importer_class
28
+ elsif default_sass_options.key?(:importer) && default_sass_options[:importer].is_a?(Importer)
29
+ default_sass_options[:importer]
30
+ else
31
+ Sprockets::Sass::V3::Importer.new
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require_relative './sass_template'
3
+ module Sprockets
4
+ module Sass
5
+ module V3
6
+ # Preprocessor for SCSS files
7
+ class ScssTemplate < Sprockets::Sass::V3::SassTemplate
8
+ # Define the expected syntax for the template
9
+ def self.syntax
10
+ :scss
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../v3/cache_store'
3
+ module Sprockets
4
+ module Sass
5
+ module V4
6
+ # Internal: Cache wrapper for Sprockets cache adapter. (Sprockets >= 3)
7
+ class CacheStore < Sprockets::Sass::V3::CacheStore
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../v3/compressor'
3
+ module Sprockets
4
+ module Sass
5
+ module V4
6
+ # Class used to compress CSS files
7
+ class Compressor < Sprockets::Sass::V3::Compressor
8
+ end
9
+ end
10
+ end
11
+ end