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.
- checksums.yaml +7 -0
- data/.reek +12 -0
- data/.rubocop.yml +77 -0
- data/.travis.yml +16 -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.rb +1 -0
- data/lib/sprockets/sass.rb +37 -12
- 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 +212 -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 +105 -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/spec/custom_importer_spec.rb +4 -6
- data/spec/spec_helper.rb +30 -3
- data/spec/sprockets-sass_spec.rb +101 -61
- data/spec/support/be_fresh_matcher.rb +10 -6
- data/spec/support/dummy_importer.rb +1 -1
- data/spec/support/fail_postprocessor.rb +23 -0
- data/spec/support/sass_template.rb +11 -0
- data/sprockets-sass.gemspec +27 -8
- metadata +92 -95
- 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,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,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
|