oreorenasass 3.4.4 → 3.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +50 -70
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/lib/sass.rb +12 -19
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +1 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -1
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +0 -1
- data/lib/sass/css.rb +13 -11
- data/lib/sass/engine.rb +173 -424
- data/lib/sass/environment.rb +58 -148
- data/lib/sass/error.rb +14 -11
- data/lib/sass/exec.rb +703 -5
- data/lib/sass/importers/base.rb +6 -49
- data/lib/sass/importers/filesystem.rb +19 -44
- data/lib/sass/logger.rb +4 -1
- data/lib/sass/logger/base.rb +4 -2
- data/lib/sass/logger/log_level.rb +7 -3
- data/lib/sass/media.rb +23 -20
- data/lib/sass/plugin.rb +7 -7
- data/lib/sass/plugin/compiler.rb +145 -304
- data/lib/sass/plugin/configuration.rb +23 -18
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script.rb +8 -35
- data/lib/sass/script/{value/arg_list.rb → arg_list.rb} +25 -9
- data/lib/sass/script/bool.rb +18 -0
- data/lib/sass/script/color.rb +606 -0
- data/lib/sass/script/css_lexer.rb +4 -8
- data/lib/sass/script/css_parser.rb +2 -5
- data/lib/sass/script/funcall.rb +245 -0
- data/lib/sass/script/functions.rb +408 -1491
- data/lib/sass/script/interpolation.rb +79 -0
- data/lib/sass/script/lexer.rb +68 -172
- data/lib/sass/script/list.rb +85 -0
- data/lib/sass/script/literal.rb +221 -0
- data/lib/sass/script/{tree/node.rb → node.rb} +12 -22
- data/lib/sass/script/{value/null.rb → null.rb} +7 -14
- data/lib/sass/script/{value/number.rb → number.rb} +75 -152
- data/lib/sass/script/{tree/operation.rb → operation.rb} +24 -17
- data/lib/sass/script/parser.rb +110 -245
- data/lib/sass/script/string.rb +51 -0
- data/lib/sass/script/{tree/string_interpolation.rb → string_interpolation.rb} +4 -5
- data/lib/sass/script/{tree/unary_operation.rb → unary_operation.rb} +6 -6
- data/lib/sass/script/variable.rb +58 -0
- data/lib/sass/scss/css_parser.rb +3 -9
- data/lib/sass/scss/parser.rb +421 -450
- data/lib/sass/scss/rx.rb +11 -19
- data/lib/sass/scss/static_parser.rb +7 -321
- data/lib/sass/selector.rb +194 -68
- data/lib/sass/selector/abstract_sequence.rb +14 -29
- data/lib/sass/selector/comma_sequence.rb +25 -108
- data/lib/sass/selector/sequence.rb +66 -159
- data/lib/sass/selector/simple.rb +25 -23
- data/lib/sass/selector/simple_sequence.rb +63 -173
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/supports.rb +15 -13
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +3 -3
- data/lib/sass/tree/css_import_node.rb +11 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +4 -21
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/extend_node.rb +7 -14
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +4 -9
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +5 -4
- data/lib/sass/tree/media_node.rb +14 -4
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +8 -21
- data/lib/sass/tree/node.rb +12 -54
- data/lib/sass/tree/prop_node.rb +20 -39
- data/lib/sass/tree/return_node.rb +2 -3
- data/lib/sass/tree/root_node.rb +3 -19
- data/lib/sass/tree/rule_node.rb +22 -35
- data/lib/sass/tree/supports_node.rb +13 -0
- data/lib/sass/tree/trace_node.rb +1 -2
- data/lib/sass/tree/variable_node.rb +3 -9
- data/lib/sass/tree/visitors/base.rb +8 -5
- data/lib/sass/tree/visitors/check_nesting.rb +19 -49
- data/lib/sass/tree/visitors/convert.rb +56 -74
- data/lib/sass/tree/visitors/cssize.rb +74 -202
- data/lib/sass/tree/visitors/deep_copy.rb +5 -10
- data/lib/sass/tree/visitors/extend.rb +7 -7
- data/lib/sass/tree/visitors/perform.rb +185 -278
- data/lib/sass/tree/visitors/set_options.rb +6 -20
- data/lib/sass/tree/visitors/to_css.rb +81 -234
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util.rb +152 -522
- data/lib/sass/util/multibyte_string_scanner.rb +0 -2
- data/lib/sass/util/subset_map.rb +3 -4
- data/lib/sass/util/test.rb +1 -0
- data/lib/sass/version.rb +22 -20
- data/test/Gemfile +3 -0
- data/test/Gemfile.lock +10 -0
- data/test/sass/cache_test.rb +20 -62
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/conversion_test.rb +2 -296
- data/test/sass/css2sass_test.rb +4 -23
- data/test/sass/engine_test.rb +354 -411
- data/test/sass/exec_test.rb +2 -2
- data/test/sass/extend_test.rb +145 -324
- data/test/sass/functions_test.rb +86 -873
- data/test/sass/importer_test.rb +21 -241
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/plugin_test.rb +26 -16
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +7 -36
- data/test/sass/script_test.rb +53 -485
- data/test/sass/scss/css_test.rb +28 -143
- data/test/sass/scss/rx_test.rb +4 -4
- data/test/sass/scss/scss_test.rb +325 -2119
- data/test/sass/templates/scss_import.scss +1 -2
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +1 -1
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +1 -86
- data/test/test_helper.rb +8 -37
- metadata +19 -66
- data/lib/sass/exec/base.rb +0 -187
- data/lib/sass/exec/sass_convert.rb +0 -264
- data/lib/sass/exec/sass_scss.rb +0 -424
- data/lib/sass/features.rb +0 -47
- data/lib/sass/script/tree.rb +0 -16
- data/lib/sass/script/tree/funcall.rb +0 -306
- data/lib/sass/script/tree/interpolation.rb +0 -118
- data/lib/sass/script/tree/list_literal.rb +0 -77
- data/lib/sass/script/tree/literal.rb +0 -45
- data/lib/sass/script/tree/map_literal.rb +0 -64
- data/lib/sass/script/tree/selector.rb +0 -26
- data/lib/sass/script/tree/variable.rb +0 -57
- data/lib/sass/script/value.rb +0 -11
- data/lib/sass/script/value/base.rb +0 -240
- data/lib/sass/script/value/bool.rb +0 -35
- data/lib/sass/script/value/color.rb +0 -680
- data/lib/sass/script/value/helpers.rb +0 -262
- data/lib/sass/script/value/list.rb +0 -113
- data/lib/sass/script/value/map.rb +0 -70
- data/lib/sass/script/value/string.rb +0 -97
- data/lib/sass/selector/pseudo.rb +0 -256
- data/lib/sass/source/map.rb +0 -210
- data/lib/sass/source/position.rb +0 -39
- data/lib/sass/source/range.rb +0 -41
- data/lib/sass/stack.rb +0 -120
- data/lib/sass/tree/at_root_node.rb +0 -83
- data/lib/sass/tree/error_node.rb +0 -18
- data/lib/sass/tree/keyframe_rule_node.rb +0 -15
- data/lib/sass/util/cross_platform_random.rb +0 -19
- data/lib/sass/util/normalized_map.rb +0 -130
- data/lib/sass/util/ordered_hash.rb +0 -192
- data/test/sass/compiler_test.rb +0 -232
- data/test/sass/encoding_test.rb +0 -219
- data/test/sass/source_map_test.rb +0 -977
- data/test/sass/superselector_test.rb +0 -191
- data/test/sass/util/normalized_map_test.rb +0 -51
- data/test/sass/value_helpers_test.rb +0 -179
data/lib/sass/importers/base.rb
CHANGED
@@ -15,9 +15,13 @@ module Sass
|
|
15
15
|
# They should also implement the \{#find\_relative} method.
|
16
16
|
#
|
17
17
|
# Importers should be serializable via `Marshal.dump`.
|
18
|
+
# In addition to the standard `_dump` and `_load` methods,
|
19
|
+
# importers can define `_before_dump`, `_after_dump`, `_around_dump`,
|
20
|
+
# and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
|
18
21
|
#
|
19
22
|
# @abstract
|
20
23
|
class Base
|
24
|
+
|
21
25
|
# Find a Sass file relative to another file.
|
22
26
|
# Importers without a notion of "relative paths"
|
23
27
|
# should just return nil here.
|
@@ -118,34 +122,6 @@ module Sass
|
|
118
122
|
Sass::Util.abstract(self)
|
119
123
|
end
|
120
124
|
|
121
|
-
# Get the publicly-visible URL for an imported file. This URL is used by
|
122
|
-
# source maps to link to the source stylesheet. This may return `nil` to
|
123
|
-
# indicate that no public URL is available; however, this will cause
|
124
|
-
# sourcemap generation to fail if any CSS is generated from files imported
|
125
|
-
# from this importer.
|
126
|
-
#
|
127
|
-
# If an absolute "file:" URI can be produced for an imported file, that
|
128
|
-
# should be preferred to returning `nil`. However, a URL relative to
|
129
|
-
# `sourcemap_directory` should be preferred over an absolute "file:" URI.
|
130
|
-
#
|
131
|
-
# @param uri [String] A URI known to be valid for this importer.
|
132
|
-
# @param sourcemap_directory [String, NilClass] The absolute path to a
|
133
|
-
# directory on disk where the sourcemap will be saved. If uri refers to
|
134
|
-
# a file on disk that's accessible relative to sourcemap_directory, this
|
135
|
-
# may return a relative URL. This may be `nil` if the sourcemap's
|
136
|
-
# eventual location is unknown.
|
137
|
-
# @return [String?] The publicly-visible URL for this file, or `nil`
|
138
|
-
# indicating that no publicly-visible URL exists. This should be
|
139
|
-
# appropriately URL-escaped.
|
140
|
-
def public_url(uri, sourcemap_directory)
|
141
|
-
return if @public_url_warning_issued
|
142
|
-
@public_url_warning_issued = true
|
143
|
-
Sass::Util.sass_warn <<WARNING
|
144
|
-
WARNING: #{self.class.name} should define the #public_url method.
|
145
|
-
WARNING
|
146
|
-
nil
|
147
|
-
end
|
148
|
-
|
149
125
|
# A string representation of the importer.
|
150
126
|
# Should be overridden by subclasses.
|
151
127
|
#
|
@@ -156,27 +132,8 @@ WARNING
|
|
156
132
|
def to_s
|
157
133
|
Sass::Util.abstract(self)
|
158
134
|
end
|
159
|
-
|
160
|
-
# If the importer is based on files on the local filesystem
|
161
|
-
# this method should return folders which should be watched
|
162
|
-
# for changes.
|
163
|
-
#
|
164
|
-
# @return [Array<String>] List of absolute paths of directories to watch
|
165
|
-
def directories_to_watch
|
166
|
-
[]
|
167
|
-
end
|
168
|
-
|
169
|
-
# If this importer is based on files on the local filesystem This method
|
170
|
-
# should return true if the file, when changed, should trigger a
|
171
|
-
# recompile.
|
172
|
-
#
|
173
|
-
# It is acceptable for non-sass files to be watched and trigger a recompile.
|
174
|
-
#
|
175
|
-
# @param filename [String] The absolute filename for a file that has changed.
|
176
|
-
# @return [Boolean] When the file changed should cause a recompile.
|
177
|
-
def watched_file?(filename)
|
178
|
-
false
|
179
|
-
end
|
180
135
|
end
|
181
136
|
end
|
182
137
|
end
|
138
|
+
|
139
|
+
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'pathname'
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
module Sass
|
@@ -5,6 +6,7 @@ module Sass
|
|
5
6
|
# The default importer, used for any strings found in the load path.
|
6
7
|
# Simply loads Sass files from the filesystem using the default logic.
|
7
8
|
class Filesystem < Base
|
9
|
+
|
8
10
|
attr_accessor :root
|
9
11
|
|
10
12
|
# Creates a new filesystem importer that imports files relative to a given path.
|
@@ -37,7 +39,7 @@ module Sass
|
|
37
39
|
# @see Base#key
|
38
40
|
def key(name, options)
|
39
41
|
[self.class.name + ":" + File.dirname(File.expand_path(name)),
|
40
|
-
|
42
|
+
File.basename(name)]
|
41
43
|
end
|
42
44
|
|
43
45
|
# @see Base#to_s
|
@@ -53,31 +55,6 @@ module Sass
|
|
53
55
|
root.eql?(other.root)
|
54
56
|
end
|
55
57
|
|
56
|
-
# @see Base#directories_to_watch
|
57
|
-
def directories_to_watch
|
58
|
-
[root]
|
59
|
-
end
|
60
|
-
|
61
|
-
# @see Base#watched_file?
|
62
|
-
def watched_file?(filename)
|
63
|
-
filename =~ /\.s[ac]ss$/ &&
|
64
|
-
filename.start_with?(root + File::SEPARATOR)
|
65
|
-
end
|
66
|
-
|
67
|
-
def public_url(name, sourcemap_directory)
|
68
|
-
file_pathname = Sass::Util.cleanpath(Sass::Util.absolute_path(name, @root))
|
69
|
-
if sourcemap_directory.nil?
|
70
|
-
Sass::Util.file_uri_from_path(file_pathname)
|
71
|
-
else
|
72
|
-
sourcemap_pathname = Sass::Util.cleanpath(sourcemap_directory)
|
73
|
-
begin
|
74
|
-
Sass::Util.file_uri_from_path(file_pathname.relative_path_from(sourcemap_pathname))
|
75
|
-
rescue ArgumentError # when a relative path cannot be constructed
|
76
|
-
Sass::Util.file_uri_from_path(file_pathname)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
58
|
protected
|
82
59
|
|
83
60
|
# If a full uri is passed, this removes the root from it
|
@@ -121,7 +98,7 @@ module Sass
|
|
121
98
|
end
|
122
99
|
|
123
100
|
# JRuby chokes when trying to import files from JARs when the path starts with './'.
|
124
|
-
ret.map {|f, s| [f.sub(
|
101
|
+
ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
|
125
102
|
end
|
126
103
|
|
127
104
|
def escape_glob_characters(name)
|
@@ -130,7 +107,7 @@ module Sass
|
|
130
107
|
end
|
131
108
|
end
|
132
109
|
|
133
|
-
REDUNDANT_DIRECTORY =
|
110
|
+
REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
|
134
111
|
# Given a base directory and an `@import`ed name,
|
135
112
|
# finds an existant file that matches the name.
|
136
113
|
#
|
@@ -138,16 +115,14 @@ module Sass
|
|
138
115
|
# @param name [String] The filename to search for.
|
139
116
|
# @return [(String, Symbol)] A filename-syntax pair.
|
140
117
|
def find_real_file(dir, name, options)
|
141
|
-
#
|
118
|
+
# on windows 'dir' can be in native File::ALT_SEPARATOR form
|
142
119
|
dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
143
|
-
name = name.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
144
120
|
|
145
121
|
found = possible_files(remove_root(name)).map do |f, s|
|
146
|
-
path = (dir == "." ||
|
147
|
-
"#{escape_glob_characters(dir)}/#{f}"
|
122
|
+
path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
|
148
123
|
Dir[path].map do |full_path|
|
149
124
|
full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
|
150
|
-
[
|
125
|
+
[Pathname.new(full_path).cleanpath.to_s, s]
|
151
126
|
end
|
152
127
|
end
|
153
128
|
found = Sass::Util.flatten(found, 1)
|
@@ -155,20 +130,20 @@ module Sass
|
|
155
130
|
|
156
131
|
if found.size > 1 && !@same_name_warnings.include?(found.first.first)
|
157
132
|
found.each {|(f, _)| @same_name_warnings << f}
|
158
|
-
relative_to =
|
159
|
-
if options[:
|
133
|
+
relative_to = Pathname.new(dir)
|
134
|
+
if options[:_line]
|
160
135
|
# If _line exists, we're here due to an actual import in an
|
161
136
|
# import_node and we want to print a warning for a user writing an
|
162
137
|
# ambiguous import.
|
163
|
-
candidates = found.map
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
Candidates:
|
138
|
+
candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
|
139
|
+
Sass::Util.sass_warn <<WARNING
|
140
|
+
WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
|
141
|
+
It's not clear which file to import for '@import "#{name}"'.
|
142
|
+
Candidates:
|
169
143
|
#{candidates}
|
170
|
-
|
171
|
-
|
144
|
+
For now I'll choose #{File.basename found.first.first}.
|
145
|
+
This will be an error in future versions of Sass.
|
146
|
+
WARNING
|
172
147
|
else
|
173
148
|
# Otherwise, we're here via StalenessChecker, and we want to print a
|
174
149
|
# warning for a user running `sass --watch` with two ambiguous files.
|
@@ -188,7 +163,7 @@ WARNING
|
|
188
163
|
def split(name)
|
189
164
|
extension = nil
|
190
165
|
dirname, basename = File.dirname(name), File.basename(name)
|
191
|
-
if basename =~ /^(.*)\.(#{extensions.keys.map
|
166
|
+
if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
|
192
167
|
basename = $1
|
193
168
|
extension = $2
|
194
169
|
end
|
data/lib/sass/logger.rb
CHANGED
data/lib/sass/logger/base.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'sass/logger/log_level'
|
2
2
|
|
3
3
|
class Sass::Logger::Base
|
4
|
+
|
4
5
|
include Sass::Logger::LogLevel
|
5
6
|
|
6
7
|
attr_accessor :log_level
|
@@ -21,10 +22,11 @@ class Sass::Logger::Base
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def log(level, message)
|
24
|
-
_log(level, message) if logging_level?(level)
|
25
|
+
self._log(level, message) if logging_level?(level)
|
25
26
|
end
|
26
27
|
|
27
28
|
def _log(level, message)
|
28
|
-
Kernel
|
29
|
+
Kernel::warn(message)
|
29
30
|
end
|
31
|
+
|
30
32
|
end
|
@@ -1,21 +1,24 @@
|
|
1
1
|
module Sass
|
2
2
|
module Logger
|
3
3
|
module LogLevel
|
4
|
+
|
4
5
|
def self.included(base)
|
5
6
|
base.extend(ClassMethods)
|
6
7
|
end
|
7
|
-
|
8
|
+
|
8
9
|
module ClassMethods
|
9
10
|
def inherited(subclass)
|
10
11
|
subclass.log_levels = subclass.superclass.log_levels.dup
|
11
12
|
end
|
12
13
|
|
13
|
-
attr_writer :log_levels
|
14
|
-
|
15
14
|
def log_levels
|
16
15
|
@log_levels ||= {}
|
17
16
|
end
|
18
17
|
|
18
|
+
def log_levels=(levels)
|
19
|
+
@log_levels = levels
|
20
|
+
end
|
21
|
+
|
19
22
|
def log_level?(level, min_level)
|
20
23
|
log_levels[level] >= log_levels[min_level]
|
21
24
|
end
|
@@ -40,6 +43,7 @@ module Sass
|
|
40
43
|
RUBY
|
41
44
|
end
|
42
45
|
end
|
46
|
+
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
data/lib/sass/media.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
module Sass::Media
|
3
3
|
# A comma-separated list of queries.
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# media_query [ ',' S* media_query ]*
|
6
6
|
class QueryList
|
7
7
|
# The queries contained in this list.
|
8
8
|
#
|
@@ -43,11 +43,11 @@ module Sass::Media
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# Returns a representation of the query as an array of strings and
|
46
|
-
# potentially {Sass::Script::
|
47
|
-
#
|
48
|
-
#
|
46
|
+
# potentially {Sass::Script::Node}s (if there's interpolation in it). When
|
47
|
+
# the interpolation is resolved and the strings are joined together, this
|
48
|
+
# will be the string representation of this query.
|
49
49
|
#
|
50
|
-
# @return [Array<String, Sass::Script::
|
50
|
+
# @return [Array<String, Sass::Script::Node>]
|
51
51
|
def to_a
|
52
52
|
Sass::Util.intersperse(queries.map {|q| q.to_a}, ', ').flatten
|
53
53
|
end
|
@@ -62,7 +62,7 @@ module Sass::Media
|
|
62
62
|
|
63
63
|
# A single media query.
|
64
64
|
#
|
65
|
-
#
|
65
|
+
# [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
|
66
66
|
class Query
|
67
67
|
# The modifier for the query.
|
68
68
|
#
|
@@ -70,7 +70,7 @@ module Sass::Media
|
|
70
70
|
# parsed as CSS, it contains a single string (accessible via
|
71
71
|
# \{#resolved_modifier}).
|
72
72
|
#
|
73
|
-
# @return [Array<String, Sass::Script::
|
73
|
+
# @return [Array<String, Sass::Script::Node>]
|
74
74
|
attr_accessor :modifier
|
75
75
|
|
76
76
|
# The type of the query (e.g. `"screen"` or `"print"`).
|
@@ -79,7 +79,7 @@ module Sass::Media
|
|
79
79
|
# parsed as CSS, it contains a single string (accessible via
|
80
80
|
# \{#resolved_type}).
|
81
81
|
#
|
82
|
-
# @return [Array<String, Sass::Script::
|
82
|
+
# @return [Array<String, Sass::Script::Node>]
|
83
83
|
attr_accessor :type
|
84
84
|
|
85
85
|
# The trailing expressions in the query.
|
@@ -87,12 +87,12 @@ module Sass::Media
|
|
87
87
|
# When parsed as Sass code, each expression contains strings and SassScript
|
88
88
|
# nodes. When parsed as CSS, each one contains a single string.
|
89
89
|
#
|
90
|
-
# @return [Array<Array<String, Sass::Script::
|
90
|
+
# @return [Array<Array<String, Sass::Script::Node>>]
|
91
91
|
attr_accessor :expressions
|
92
92
|
|
93
|
-
# @param modifier [Array<String, Sass::Script::
|
94
|
-
# @param type [Array<String, Sass::Script::
|
95
|
-
# @param expressions [Array<Array<String, Sass::Script::
|
93
|
+
# @param modifier [Array<String, Sass::Script::Node>] See \{#modifier}
|
94
|
+
# @param type [Array<String, Sass::Script::Node>] See \{#type}
|
95
|
+
# @param expressions [Array<Array<String, Sass::Script::Node>>] See \{#expressions}
|
96
96
|
def initialize(modifier, type, expressions)
|
97
97
|
@modifier = modifier
|
98
98
|
@type = type
|
@@ -125,7 +125,7 @@ module Sass::Media
|
|
125
125
|
m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
|
126
126
|
t1 = t2 if t1.empty?
|
127
127
|
t2 = t1 if t2.empty?
|
128
|
-
if (m1 == 'not') ^ (m2 == 'not')
|
128
|
+
if ((m1 == 'not') ^ (m2 == 'not'))
|
129
129
|
return if t1 == t2
|
130
130
|
type = m1 == 'not' ? t2 : t1
|
131
131
|
mod = m1 == 'not' ? m2 : m1
|
@@ -140,7 +140,7 @@ module Sass::Media
|
|
140
140
|
type = t1
|
141
141
|
mod = m1.empty? ? m2 : m1
|
142
142
|
end
|
143
|
-
Query.new([mod], [type], other.expressions + expressions)
|
143
|
+
return Query.new([mod], [type], other.expressions + expressions)
|
144
144
|
end
|
145
145
|
|
146
146
|
# Returns the CSS for the media query.
|
@@ -156,7 +156,7 @@ module Sass::Media
|
|
156
156
|
# It's possible for there to be script nodes in Expressions even when
|
157
157
|
# we're converting to CSS in the case where we parsed the document as
|
158
158
|
# CSS originally (as in css_test.rb).
|
159
|
-
e.map {|c| c.is_a?(Sass::Script::
|
159
|
+
e.map {|c| c.is_a?(Sass::Script::Node) ? c.to_sass : c.to_s}.join
|
160
160
|
end.join(' and ')
|
161
161
|
css
|
162
162
|
end
|
@@ -193,18 +193,21 @@ module Sass::Media
|
|
193
193
|
# @return [Query]
|
194
194
|
def deep_copy
|
195
195
|
Query.new(
|
196
|
-
modifier.map {|c| c.is_a?(Sass::Script::
|
197
|
-
type.map {|c| c.is_a?(Sass::Script::
|
198
|
-
expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::
|
196
|
+
modifier.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
197
|
+
type.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
198
|
+
expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}})
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
202
202
|
# Converts an interpolation array to source.
|
203
203
|
#
|
204
|
-
# @param
|
204
|
+
# @param [Array<String, Sass::Script::Node>] The interpolation array to convert.
|
205
205
|
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
206
206
|
# @return [String]
|
207
207
|
def self._interp_to_src(interp, options)
|
208
|
-
interp.map
|
208
|
+
interp.map do |r|
|
209
|
+
next r if r.is_a?(String)
|
210
|
+
"\#{#{r.to_sass(options)}}"
|
211
|
+
end.join
|
209
212
|
end
|
210
213
|
end
|
data/lib/sass/plugin.rb
CHANGED
@@ -11,8 +11,7 @@ module Sass
|
|
11
11
|
# This module is used as the primary interface with Sass
|
12
12
|
# when it's used as a plugin for various frameworks.
|
13
13
|
# All Rack-enabled frameworks are supported out of the box.
|
14
|
-
# The plugin is
|
15
|
-
# {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
|
14
|
+
# The plugin is {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
|
16
15
|
# Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
|
17
16
|
#
|
18
17
|
# This module has a large set of callbacks available
|
@@ -33,6 +32,7 @@ module Sass
|
|
33
32
|
# #=> Compiling app/sass/ie.scss to public/stylesheets/ie.css
|
34
33
|
# @see Sass::Plugin::Compiler
|
35
34
|
module Plugin
|
35
|
+
include Sass::Util
|
36
36
|
extend self
|
37
37
|
|
38
38
|
@checked_for_updates = false
|
@@ -65,8 +65,7 @@ module Sass
|
|
65
65
|
|
66
66
|
# Updates out-of-date stylesheets.
|
67
67
|
#
|
68
|
-
# Checks each Sass/SCSS file in
|
69
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
68
|
+
# Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
70
69
|
# to see if it's been modified more recently than the corresponding CSS file
|
71
70
|
# in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
|
72
71
|
# If it has, it updates the CSS file.
|
@@ -120,12 +119,13 @@ module Sass
|
|
120
119
|
def options
|
121
120
|
compiler.options
|
122
121
|
end
|
122
|
+
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
require 'sass/plugin/rails'
|
126
|
+
# On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
|
127
|
+
if defined?(ActionController) && !Sass::Util.ap_geq_3?
|
128
|
+
require 'sass/plugin/rails'
|
129
129
|
elsif defined?(Merb::Plugins)
|
130
130
|
require 'sass/plugin/merb'
|
131
131
|
else
|
data/lib/sass/plugin/compiler.rb
CHANGED
@@ -7,6 +7,7 @@ require 'sass/plugin/configuration'
|
|
7
7
|
require 'sass/plugin/staleness_checker'
|
8
8
|
|
9
9
|
module Sass::Plugin
|
10
|
+
|
10
11
|
# The Compiler class handles compilation of multiple files and/or directories,
|
11
12
|
# including checking which CSS files are out-of-date and need to be updated
|
12
13
|
# and calling Sass to perform the compilation on those files.
|
@@ -25,41 +26,31 @@ module Sass::Plugin
|
|
25
26
|
# * `:never_update`
|
26
27
|
# * `:always_check`
|
27
28
|
class Compiler
|
29
|
+
include Sass::Util
|
28
30
|
include Configuration
|
29
31
|
extend Sass::Callbacks
|
30
32
|
|
31
33
|
# Creates a new compiler.
|
32
34
|
#
|
33
|
-
# @param
|
35
|
+
# @param options [{Symbol => Object}]
|
34
36
|
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
35
|
-
def initialize(
|
36
|
-
options.merge!(
|
37
|
+
def initialize(options = {})
|
38
|
+
self.options.merge!(options)
|
37
39
|
end
|
38
40
|
|
39
|
-
# Register a callback to be run
|
41
|
+
# Register a callback to be run after stylesheets are mass-updated.
|
40
42
|
# This is run whenever \{#update\_stylesheets} is called,
|
41
43
|
# unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
|
42
44
|
# is enabled.
|
43
45
|
#
|
44
|
-
# @yield [
|
45
|
-
# @yieldparam
|
46
|
-
# Individual files to be updated
|
46
|
+
# @yield [individual_files]
|
47
|
+
# @yieldparam individual_files [<(String, String)>]
|
48
|
+
# Individual files to be updated, in addition to the directories
|
49
|
+
# specified in the options.
|
47
50
|
# The first element of each pair is the source file,
|
48
|
-
# the second is the target CSS file
|
49
|
-
# the third is the target sourcemap file.
|
51
|
+
# the second is the target CSS file.
|
50
52
|
define_callback :updating_stylesheets
|
51
53
|
|
52
|
-
# Register a callback to be run after stylesheets are mass-updated.
|
53
|
-
# This is run whenever \{#update\_stylesheets} is called,
|
54
|
-
# unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
|
55
|
-
# is enabled.
|
56
|
-
#
|
57
|
-
# @yield [updated_files]
|
58
|
-
# @yieldparam updated_files [<(String, String)>]
|
59
|
-
# Individual files that were updated.
|
60
|
-
# The first element of each pair is the source file, the second is the target CSS file.
|
61
|
-
define_callback :updated_stylesheets
|
62
|
-
|
63
54
|
# Register a callback to be run after a single stylesheet is updated.
|
64
55
|
# The callback is only run if the stylesheet is really updated;
|
65
56
|
# if the CSS file is fresh, this won't be run.
|
@@ -69,29 +60,35 @@ module Sass::Plugin
|
|
69
60
|
# when an exception CSS file is being written.
|
70
61
|
# To run an action for those files, use \{#on\_compilation\_error}.
|
71
62
|
#
|
72
|
-
# @yield [template, css
|
63
|
+
# @yield [template, css]
|
73
64
|
# @yieldparam template [String]
|
74
65
|
# The location of the Sass/SCSS file being updated.
|
75
66
|
# @yieldparam css [String]
|
76
67
|
# The location of the CSS file being generated.
|
77
|
-
# @yieldparam sourcemap [String]
|
78
|
-
# The location of the sourcemap being generated, if any.
|
79
68
|
define_callback :updated_stylesheet
|
80
69
|
|
81
|
-
# Register a callback to be run
|
70
|
+
# Register a callback to be run before a single stylesheet is updated.
|
71
|
+
# The callback is only run if the stylesheet is guaranteed to be updated;
|
72
|
+
# if the CSS file is fresh, this won't be run.
|
82
73
|
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
74
|
+
# Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
|
75
|
+
# is enabled, this callback won't be run
|
76
|
+
# when an exception CSS file is being written.
|
77
|
+
# To run an action for those files, use \{#on\_compilation\_error}.
|
86
78
|
#
|
87
|
-
# @yield [template, css
|
79
|
+
# @yield [template, css]
|
88
80
|
# @yieldparam template [String]
|
89
81
|
# The location of the Sass/SCSS file being updated.
|
90
82
|
# @yieldparam css [String]
|
91
83
|
# The location of the CSS file being generated.
|
92
|
-
|
93
|
-
|
94
|
-
|
84
|
+
define_callback :updating_stylesheet
|
85
|
+
|
86
|
+
def on_updating_stylesheet_with_deprecation_warning(&block)
|
87
|
+
Sass::Util.sass_warn("Sass::Compiler#on_updating_stylesheet callback is deprecated and will be removed in a future release. Use Sass::Compiler#on_updated_stylesheet instead, which is run after stylesheet compilation.")
|
88
|
+
on_updating_stylesheet_without_deprecation_warning(&block)
|
89
|
+
end
|
90
|
+
alias_method :on_updating_stylesheet_without_deprecation_warning, :on_updating_stylesheet
|
91
|
+
alias_method :on_updating_stylesheet, :on_updating_stylesheet_with_deprecation_warning
|
95
92
|
|
96
93
|
# Register a callback to be run when Sass decides not to update a stylesheet.
|
97
94
|
# In particular, the callback is run when Sass finds that
|
@@ -165,97 +162,49 @@ module Sass::Plugin
|
|
165
162
|
define_callback :template_deleted
|
166
163
|
|
167
164
|
# Register a callback to be run when Sass deletes a CSS file.
|
168
|
-
# This happens when the corresponding Sass/SCSS file has been deleted
|
169
|
-
# and when the compiler cleans the output files.
|
165
|
+
# This happens when the corresponding Sass/SCSS file has been deleted.
|
170
166
|
#
|
171
167
|
# @yield [filename]
|
172
168
|
# @yieldparam filename [String]
|
173
169
|
# The location of the CSS file that was deleted.
|
174
170
|
define_callback :deleting_css
|
175
171
|
|
176
|
-
# Register a callback to be run when Sass deletes a sourcemap file.
|
177
|
-
# This happens when the corresponding Sass/SCSS file has been deleted
|
178
|
-
# and when the compiler cleans the output files.
|
179
|
-
#
|
180
|
-
# @yield [filename]
|
181
|
-
# @yieldparam filename [String]
|
182
|
-
# The location of the sourcemap file that was deleted.
|
183
|
-
define_callback :deleting_sourcemap
|
184
|
-
|
185
172
|
# Updates out-of-date stylesheets.
|
186
173
|
#
|
187
|
-
# Checks each Sass/SCSS file in
|
188
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
174
|
+
# Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
189
175
|
# to see if it's been modified more recently than the corresponding CSS file
|
190
176
|
# in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
|
191
177
|
# If it has, it updates the CSS file.
|
192
178
|
#
|
193
|
-
# @param individual_files [Array<(String, String
|
179
|
+
# @param individual_files [Array<(String, String)>]
|
194
180
|
# A list of files to check for updates
|
195
181
|
# **in addition to those specified by the
|
196
182
|
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
197
183
|
# The first string in each pair is the location of the Sass/SCSS file,
|
198
184
|
# the second is the location of the CSS file that it should be compiled to.
|
199
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
200
185
|
def update_stylesheets(individual_files = [])
|
186
|
+
individual_files = individual_files.dup
|
201
187
|
Sass::Plugin.checked_for_updates = true
|
202
188
|
staleness_checker = StalenessChecker.new(engine_options)
|
203
189
|
|
204
|
-
files = file_list(individual_files)
|
205
|
-
run_updating_stylesheets(files)
|
206
|
-
|
207
|
-
updated_stylesheets = []
|
208
|
-
files.each do |file, css, sourcemap|
|
209
|
-
# TODO: Does staleness_checker need to check the sourcemap file as well?
|
210
|
-
if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
|
211
|
-
# XXX For consistency, this should return the sourcemap too, but it would
|
212
|
-
# XXX be an API change.
|
213
|
-
updated_stylesheets << [file, css]
|
214
|
-
update_stylesheet(file, css, sourcemap)
|
215
|
-
else
|
216
|
-
run_not_updating_stylesheet(file, css, sourcemap)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
run_updated_stylesheets(updated_stylesheets)
|
220
|
-
end
|
221
|
-
|
222
|
-
# Construct a list of files that might need to be compiled
|
223
|
-
# from the provided individual_files and the template_locations.
|
224
|
-
#
|
225
|
-
# Note: this method does not cache the results as they can change
|
226
|
-
# across invocations when sass files are added or removed.
|
227
|
-
#
|
228
|
-
# @param individual_files [Array<(String, String[, String])>]
|
229
|
-
# A list of files to check for updates
|
230
|
-
# **in addition to those specified by the
|
231
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
232
|
-
# The first string in each pair is the location of the Sass/SCSS file,
|
233
|
-
# the second is the location of the CSS file that it should be compiled to.
|
234
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
235
|
-
# @return [Array<(String, String, String)>]
|
236
|
-
# A list of [sass_file, css_file, sourcemap_file] tuples similar
|
237
|
-
# to what was passed in, but expanded to include the current state
|
238
|
-
# of the directories being updated.
|
239
|
-
def file_list(individual_files = [])
|
240
|
-
files = individual_files.map do |tuple|
|
241
|
-
if tuple.size < 3
|
242
|
-
[tuple[0], tuple[1], Sass::Util.sourcemap_name(tuple[1])]
|
243
|
-
else
|
244
|
-
tuple
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
190
|
template_location_array.each do |template_location, css_location|
|
249
191
|
Sass::Util.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
|
250
192
|
# Get the relative path to the file
|
251
|
-
name =
|
252
|
-
Sass::Util.pathname(template_location.to_s)).to_s
|
193
|
+
name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
|
253
194
|
css = css_filename(name, css_location)
|
254
|
-
|
255
|
-
|
195
|
+
individual_files << [file, css]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
run_updating_stylesheets individual_files
|
200
|
+
|
201
|
+
individual_files.each do |file, css|
|
202
|
+
if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
|
203
|
+
update_stylesheet(file, css)
|
204
|
+
else
|
205
|
+
run_not_updating_stylesheet(file, css)
|
256
206
|
end
|
257
207
|
end
|
258
|
-
files
|
259
208
|
end
|
260
209
|
|
261
210
|
# Watches the template directory (or directories)
|
@@ -276,62 +225,73 @@ module Sass::Plugin
|
|
276
225
|
# The version of Listen distributed with Sass is loaded by default,
|
277
226
|
# but if another version has already been loaded that will be used instead.
|
278
227
|
#
|
279
|
-
# @param individual_files [Array<(String, String
|
280
|
-
# A list of files to
|
228
|
+
# @param individual_files [Array<(String, String)>]
|
229
|
+
# A list of files to watch for updates
|
281
230
|
# **in addition to those specified by the
|
282
231
|
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
283
232
|
# The first string in each pair is the location of the Sass/SCSS file,
|
284
233
|
# the second is the location of the CSS file that it should be compiled to.
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
def watch(individual_files = [], options = {})
|
290
|
-
options, individual_files = individual_files, [] if individual_files.is_a?(Hash)
|
291
|
-
update_stylesheets(individual_files) unless options[:skip_initial_update]
|
292
|
-
|
293
|
-
directories = watched_paths
|
294
|
-
individual_files.each do |(source, _, _)|
|
295
|
-
directories << File.dirname(File.expand_path(source))
|
296
|
-
end
|
297
|
-
directories = remove_redundant_directories(directories)
|
234
|
+
def watch(individual_files = [])
|
235
|
+
update_stylesheets(individual_files)
|
236
|
+
|
237
|
+
load_listen!
|
298
238
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
239
|
+
template_paths = template_locations # cache the locations
|
240
|
+
individual_files_hash = individual_files.inject({}) do |h, files|
|
241
|
+
parent = File.dirname(files.first)
|
242
|
+
(h[parent] ||= []) << files unless template_paths.include?(parent)
|
243
|
+
h
|
304
244
|
end
|
245
|
+
directories = template_paths + individual_files_hash.keys +
|
246
|
+
[{:relative_paths => true}]
|
305
247
|
|
306
248
|
# TODO: Keep better track of what depends on what
|
307
249
|
# so we don't have to run a global update every time anything changes.
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
# In Listen 2.0.0 and on, :force_polling is an option. In earlier
|
319
|
-
# versions, it's a method on the listener (called below).
|
320
|
-
listener_args.last[:force_polling] = true
|
321
|
-
end
|
250
|
+
listener = Listen::MultiListener.new(*directories) do |modified, added, removed|
|
251
|
+
modified.each do |f|
|
252
|
+
parent = File.dirname(f)
|
253
|
+
if files = individual_files_hash[parent]
|
254
|
+
next unless files.first == f
|
255
|
+
else
|
256
|
+
next unless f =~ /\.s[ac]ss$/
|
257
|
+
end
|
258
|
+
run_template_modified(f)
|
259
|
+
end
|
322
260
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
261
|
+
added.each do |f|
|
262
|
+
parent = File.dirname(f)
|
263
|
+
if files = individual_files_hash[parent]
|
264
|
+
next unless files.first == f
|
265
|
+
else
|
266
|
+
next unless f =~ /\.s[ac]ss$/
|
267
|
+
end
|
268
|
+
run_template_created(f)
|
269
|
+
end
|
327
270
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
271
|
+
removed.each do |f|
|
272
|
+
parent = File.dirname(f)
|
273
|
+
if files = individual_files_hash[parent]
|
274
|
+
next unless files.first == f
|
275
|
+
try_delete_css files[1]
|
276
|
+
else
|
277
|
+
next unless f =~ /\.s[ac]ss$/
|
278
|
+
try_delete_css f.gsub(/\.s[ac]ss$/, '.css')
|
279
|
+
end
|
280
|
+
run_template_deleted(f)
|
281
|
+
end
|
282
|
+
|
283
|
+
update_stylesheets(individual_files)
|
332
284
|
end
|
333
285
|
|
334
|
-
|
286
|
+
# The native windows listener is much slower than the polling
|
287
|
+
# option, according to https://github.com/nex3/sass/commit/a3031856b22bc834a5417dedecb038b7be9b9e3e#commitcomment-1295118
|
288
|
+
listener.force_polling(true) if @options[:poll] || Sass::Util.windows?
|
289
|
+
|
290
|
+
begin
|
291
|
+
listener.start
|
292
|
+
rescue Exception => e
|
293
|
+
raise e unless e.is_a?(Interrupt)
|
294
|
+
end
|
335
295
|
end
|
336
296
|
|
337
297
|
# Non-destructively modifies \{#options} so that default values are properly set,
|
@@ -342,8 +302,6 @@ module Sass::Plugin
|
|
342
302
|
def engine_options(additional_options = {})
|
343
303
|
opts = options.merge(additional_options)
|
344
304
|
opts[:load_paths] = load_paths(opts)
|
345
|
-
options[:sourcemap] = :auto if options[:sourcemap] == true
|
346
|
-
options[:sourcemap] = :none if options[:sourcemap] == false
|
347
305
|
opts
|
348
306
|
end
|
349
307
|
|
@@ -352,186 +310,81 @@ module Sass::Plugin
|
|
352
310
|
StalenessChecker.stylesheet_needs_update?(css_file, template_file)
|
353
311
|
end
|
354
312
|
|
355
|
-
# Remove all output files that would be created by calling update_stylesheets, if they exist.
|
356
|
-
#
|
357
|
-
# This method runs the deleting_css and deleting_sourcemap callbacks for
|
358
|
-
# the files that are deleted.
|
359
|
-
#
|
360
|
-
# @param individual_files [Array<(String, String[, String])>]
|
361
|
-
# A list of files to check for updates
|
362
|
-
# **in addition to those specified by the
|
363
|
-
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
364
|
-
# The first string in each pair is the location of the Sass/SCSS file,
|
365
|
-
# the second is the location of the CSS file that it should be compiled to.
|
366
|
-
# The third string, if provided, is the location of the Sourcemap file.
|
367
|
-
def clean(individual_files = [])
|
368
|
-
file_list(individual_files).each do |(_, css_file, sourcemap_file)|
|
369
|
-
if File.exist?(css_file)
|
370
|
-
run_deleting_css css_file
|
371
|
-
File.delete(css_file)
|
372
|
-
end
|
373
|
-
if sourcemap_file && File.exist?(sourcemap_file)
|
374
|
-
run_deleting_sourcemap sourcemap_file
|
375
|
-
File.delete(sourcemap_file)
|
376
|
-
end
|
377
|
-
end
|
378
|
-
nil
|
379
|
-
end
|
380
|
-
|
381
313
|
private
|
382
314
|
|
383
|
-
def
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
315
|
+
def load_listen!
|
316
|
+
if defined?(gem)
|
317
|
+
begin
|
318
|
+
gem 'listen', '~> 0.7'
|
319
|
+
require 'listen'
|
320
|
+
rescue Gem::LoadError
|
321
|
+
dir = Sass::Util.scope("vendor/listen/lib")
|
322
|
+
$LOAD_PATH.unshift dir
|
323
|
+
begin
|
324
|
+
require 'listen'
|
325
|
+
rescue LoadError => e
|
326
|
+
e.message << "\n" <<
|
327
|
+
if File.exists?(scope(".git"))
|
328
|
+
'Run "git submodule update --init" to get the recommended version.'
|
329
|
+
else
|
330
|
+
'Run "gem install listen" to get it.'
|
331
|
+
end
|
332
|
+
raise e
|
333
|
+
end
|
390
334
|
end
|
391
335
|
else
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
# Squelch Interrupt for clean exit from Listen::Listener
|
404
|
-
end
|
405
|
-
|
406
|
-
def remove_redundant_directories(directories)
|
407
|
-
dedupped = []
|
408
|
-
directories.each do |new_directory|
|
409
|
-
# no need to add a directory that is already watched.
|
410
|
-
next if dedupped.any? do |existing_directory|
|
411
|
-
child_of_directory?(existing_directory, new_directory)
|
412
|
-
end
|
413
|
-
# get rid of any sub directories of this new directory
|
414
|
-
dedupped.reject! do |existing_directory|
|
415
|
-
child_of_directory?(new_directory, existing_directory)
|
416
|
-
end
|
417
|
-
dedupped << new_directory
|
418
|
-
end
|
419
|
-
dedupped
|
420
|
-
end
|
421
|
-
|
422
|
-
def on_file_changed(individual_files, modified, added, removed)
|
423
|
-
recompile_required = false
|
424
|
-
|
425
|
-
modified.uniq.each do |f|
|
426
|
-
next unless watched_file?(f)
|
427
|
-
recompile_required = true
|
428
|
-
run_template_modified(relative_to_pwd(f))
|
429
|
-
end
|
430
|
-
|
431
|
-
added.uniq.each do |f|
|
432
|
-
next unless watched_file?(f)
|
433
|
-
recompile_required = true
|
434
|
-
run_template_created(relative_to_pwd(f))
|
435
|
-
end
|
436
|
-
|
437
|
-
removed.uniq.each do |f|
|
438
|
-
run_template_deleted(relative_to_pwd(f))
|
439
|
-
if (files = individual_files.find {|(source, _, _)| File.expand_path(source) == f})
|
440
|
-
recompile_required = true
|
441
|
-
# This was a file we were watching explicitly and compiling to a particular location.
|
442
|
-
# Delete the corresponding file.
|
443
|
-
try_delete_css files[1]
|
444
|
-
else
|
445
|
-
next unless watched_file?(f)
|
446
|
-
recompile_required = true
|
447
|
-
# Look for the sass directory that contained the sass file
|
448
|
-
# And try to remove the css file that corresponds to it
|
449
|
-
template_location_array.each do |(sass_dir, css_dir)|
|
450
|
-
sass_dir = File.expand_path(sass_dir)
|
451
|
-
if child_of_directory?(sass_dir, f)
|
452
|
-
remainder = f[(sass_dir.size + 1)..-1]
|
453
|
-
try_delete_css(css_filename(remainder, css_dir))
|
454
|
-
break
|
455
|
-
end
|
336
|
+
begin
|
337
|
+
require 'listen'
|
338
|
+
rescue LoadError => e
|
339
|
+
dir = Sass::Util.scope("vendor/listen/lib")
|
340
|
+
if $LOAD_PATH.include?(dir)
|
341
|
+
raise e unless File.exists?(scope(".git"))
|
342
|
+
e.message << "\n" <<
|
343
|
+
'Run "git submodule update --init" to get the recommended version.'
|
344
|
+
else
|
345
|
+
$LOAD_PATH.unshift dir
|
346
|
+
retry
|
456
347
|
end
|
457
348
|
end
|
458
349
|
end
|
459
|
-
|
460
|
-
if recompile_required
|
461
|
-
# In case a file we're watching is removed and then recreated we
|
462
|
-
# prune out the non-existant files here.
|
463
|
-
watched_files_remaining = individual_files.select {|(source, _, _)| File.exist?(source)}
|
464
|
-
update_stylesheets(watched_files_remaining)
|
465
|
-
end
|
466
350
|
end
|
467
351
|
|
468
|
-
def update_stylesheet(filename, css
|
352
|
+
def update_stylesheet(filename, css)
|
469
353
|
dir = File.dirname(css)
|
470
|
-
unless File.
|
354
|
+
unless File.exists?(dir)
|
471
355
|
run_creating_directory dir
|
472
356
|
FileUtils.mkdir_p dir
|
473
357
|
end
|
474
358
|
|
475
359
|
begin
|
476
360
|
File.read(filename) unless File.readable?(filename) # triggers an error for handling
|
477
|
-
engine_opts = engine_options(:css_filename => css,
|
478
|
-
|
479
|
-
|
480
|
-
mapping = nil
|
481
|
-
run_compilation_starting(filename, css, sourcemap)
|
482
|
-
engine = Sass::Engine.for_file(filename, engine_opts)
|
483
|
-
if sourcemap
|
484
|
-
rendered, mapping = engine.render_with_sourcemap(File.basename(sourcemap))
|
485
|
-
else
|
486
|
-
rendered = engine.render
|
487
|
-
end
|
488
|
-
rescue StandardError => e
|
361
|
+
engine_opts = engine_options(:css_filename => css, :filename => filename)
|
362
|
+
result = Sass::Engine.for_file(filename, engine_opts).render
|
363
|
+
rescue Exception => e
|
489
364
|
compilation_error_occured = true
|
490
|
-
run_compilation_error e, filename, css
|
491
|
-
|
492
|
-
|
365
|
+
run_compilation_error e, filename, css
|
366
|
+
result = Sass::SyntaxError.exception_to_css(e, options)
|
367
|
+
else
|
368
|
+
run_updating_stylesheet filename, css
|
493
369
|
end
|
494
370
|
|
495
|
-
write_file(css,
|
496
|
-
|
497
|
-
write_file(sourcemap, mapping.to_json(
|
498
|
-
:css_path => css, :sourcemap_path => sourcemap, :type => options[:sourcemap]))
|
499
|
-
end
|
500
|
-
run_updated_stylesheet(filename, css, sourcemap) unless compilation_error_occured
|
371
|
+
write_file(css, result)
|
372
|
+
run_updated_stylesheet(filename, css) unless compilation_error_occured
|
501
373
|
end
|
502
374
|
|
503
|
-
def write_file(
|
375
|
+
def write_file(css, content)
|
504
376
|
flag = 'w'
|
505
377
|
flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
|
506
|
-
File.open(
|
378
|
+
File.open(css, flag) do |file|
|
507
379
|
file.set_encoding(content.encoding) unless Sass::Util.ruby1_8?
|
508
380
|
file.print(content)
|
509
381
|
end
|
510
382
|
end
|
511
383
|
|
512
384
|
def try_delete_css(css)
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
end
|
517
|
-
map = Sass::Util.sourcemap_name(css)
|
518
|
-
if File.exist?(map)
|
519
|
-
run_deleting_sourcemap map
|
520
|
-
File.delete map
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
def watched_file?(file)
|
525
|
-
normalized_load_paths.find {|lp| lp.watched_file?(file)}
|
526
|
-
end
|
527
|
-
|
528
|
-
def watched_paths
|
529
|
-
@watched_paths ||= normalized_load_paths.map {|lp| lp.directories_to_watch}.compact.flatten
|
530
|
-
end
|
531
|
-
|
532
|
-
def normalized_load_paths
|
533
|
-
@normalized_load_paths ||=
|
534
|
-
Sass::Engine.normalize_options(:load_paths => load_paths)[:load_paths]
|
385
|
+
return unless File.exists?(css)
|
386
|
+
run_deleting_css css
|
387
|
+
File.delete css
|
535
388
|
end
|
536
389
|
|
537
390
|
def load_paths(opts = options)
|
@@ -547,19 +400,7 @@ module Sass::Plugin
|
|
547
400
|
end
|
548
401
|
|
549
402
|
def css_filename(name, path)
|
550
|
-
"#{path}
|
551
|
-
gsub(/\.s[ac]ss$/, '.css')
|
552
|
-
end
|
553
|
-
|
554
|
-
def relative_to_pwd(f)
|
555
|
-
Sass::Util.pathname(f).relative_path_from(Sass::Util.pathname(Dir.pwd)).to_s
|
556
|
-
rescue ArgumentError # when a relative path cannot be computed
|
557
|
-
f
|
558
|
-
end
|
559
|
-
|
560
|
-
def child_of_directory?(parent, child)
|
561
|
-
parent_dir = parent.end_with?(File::SEPARATOR) ? parent : (parent + File::SEPARATOR)
|
562
|
-
child.start_with?(parent_dir) || parent == child
|
403
|
+
"#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
|
563
404
|
end
|
564
405
|
end
|
565
406
|
end
|