haml 2.2.24 → 3.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/.yardopts +0 -1
- data/README.md +91 -151
- data/REMEMBER +11 -1
- data/Rakefile +73 -55
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/css2sass +7 -1
- data/bin/sass-convert +7 -0
- data/extra/haml-mode.el +2 -1
- data/lib/haml/buffer.rb +22 -4
- data/lib/haml/engine.rb +5 -1
- data/lib/haml/exec.rb +231 -46
- data/lib/haml/filters.rb +19 -8
- data/lib/haml/helpers.rb +47 -20
- data/lib/haml/helpers/action_view_extensions.rb +2 -4
- data/lib/haml/helpers/action_view_mods.rb +11 -8
- data/lib/haml/helpers/xss_mods.rb +13 -2
- data/lib/haml/html.rb +179 -48
- data/lib/haml/html/erb.rb +141 -0
- data/lib/haml/precompiler.rb +40 -15
- data/lib/haml/railtie.rb +1 -5
- data/lib/haml/root.rb +3 -0
- data/lib/haml/template.rb +4 -14
- data/lib/haml/util.rb +120 -30
- data/lib/haml/version.rb +25 -2
- data/lib/sass.rb +5 -1
- data/lib/sass/callbacks.rb +50 -0
- data/lib/sass/css.rb +40 -191
- data/lib/sass/engine.rb +170 -74
- data/lib/sass/environment.rb +8 -2
- data/lib/sass/error.rb +163 -25
- data/lib/sass/files.rb +31 -28
- data/lib/sass/plugin.rb +268 -87
- data/lib/sass/plugin/rails.rb +9 -4
- data/lib/sass/repl.rb +1 -1
- data/lib/sass/script.rb +31 -29
- data/lib/sass/script/bool.rb +1 -0
- data/lib/sass/script/color.rb +290 -23
- data/lib/sass/script/css_lexer.rb +22 -0
- data/lib/sass/script/css_parser.rb +28 -0
- data/lib/sass/script/funcall.rb +22 -3
- data/lib/sass/script/functions.rb +523 -33
- data/lib/sass/script/interpolation.rb +42 -0
- data/lib/sass/script/lexer.rb +169 -52
- data/lib/sass/script/literal.rb +58 -9
- data/lib/sass/script/node.rb +79 -1
- data/lib/sass/script/number.rb +20 -5
- data/lib/sass/script/operation.rb +49 -3
- data/lib/sass/script/parser.rb +162 -28
- data/lib/sass/script/string.rb +50 -2
- data/lib/sass/script/unary_operation.rb +25 -2
- data/lib/sass/script/variable.rb +21 -4
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/scss/css_parser.rb +39 -0
- data/lib/sass/scss/parser.rb +683 -0
- data/lib/sass/scss/rx.rb +112 -0
- data/lib/sass/scss/script_lexer.rb +13 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/tree/comment_node.rb +69 -27
- data/lib/sass/tree/debug_node.rb +7 -2
- data/lib/sass/tree/directive_node.rb +41 -35
- data/lib/sass/tree/for_node.rb +6 -0
- data/lib/sass/tree/if_node.rb +13 -1
- data/lib/sass/tree/import_node.rb +52 -27
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +41 -6
- data/lib/sass/tree/node.rb +197 -70
- data/lib/sass/tree/prop_node.rb +152 -57
- data/lib/sass/tree/root_node.rb +118 -0
- data/lib/sass/tree/rule_node.rb +193 -96
- data/lib/sass/tree/variable_node.rb +9 -5
- data/lib/sass/tree/while_node.rb +4 -0
- data/test/benchmark.rb +5 -5
- data/test/haml/engine_test.rb +147 -10
- data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
- data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
- data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
- data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
- data/test/haml/helper_test.rb +91 -24
- data/test/haml/html2haml/erb_tests.rb +410 -0
- data/test/haml/html2haml_test.rb +210 -66
- data/test/haml/results/filters.xhtml +1 -1
- data/test/haml/results/just_stuff.xhtml +2 -0
- data/test/haml/spec_test.rb +44 -0
- data/test/haml/template_test.rb +22 -2
- data/test/haml/templates/helpers.haml +0 -13
- data/test/haml/templates/just_stuff.haml +2 -0
- data/test/haml/util_test.rb +48 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +884 -0
- data/test/sass/css2sass_test.rb +99 -18
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +1049 -131
- data/test/sass/functions_test.rb +398 -47
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more_import.sass +3 -3
- data/test/sass/plugin_test.rb +184 -10
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +5 -5
- data/test/sass/results/compressed.css +1 -1
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +3 -1
- data/test/sass/results/mixins.css +12 -12
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +4 -4
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +15 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/script_conversion_test.rb +153 -0
- data/test/sass/script_test.rb +137 -70
- data/test/sass/scss/css_test.rb +811 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +871 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/alt.sass +2 -2
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/import.sass +4 -4
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/line_numbers.sass +1 -1
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +2 -2
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +2 -2
- data/test/sass/templates/script.sass +69 -69
- data/test/sass/templates/scss_import.scss +10 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/units.sass +10 -10
- data/test/test_helper.rb +20 -8
- data/vendor/fssm/LICENSE +20 -0
- data/vendor/fssm/README.markdown +55 -0
- data/vendor/fssm/Rakefile +59 -0
- data/vendor/fssm/VERSION.yml +5 -0
- data/vendor/fssm/example.rb +9 -0
- data/vendor/fssm/fssm.gemspec +77 -0
- data/vendor/fssm/lib/fssm.rb +33 -0
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
- data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
- data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
- data/vendor/fssm/lib/fssm/monitor.rb +26 -0
- data/vendor/fssm/lib/fssm/path.rb +91 -0
- data/vendor/fssm/lib/fssm/pathname.rb +502 -0
- data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
- data/vendor/fssm/lib/fssm/state/file.rb +24 -0
- data/vendor/fssm/lib/fssm/support.rb +63 -0
- data/vendor/fssm/lib/fssm/tree.rb +176 -0
- data/vendor/fssm/profile/prof-cache.rb +40 -0
- data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
- data/vendor/fssm/profile/prof-pathname.rb +68 -0
- data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
- data/vendor/fssm/profile/prof.html +2379 -0
- data/vendor/fssm/spec/path_spec.rb +75 -0
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +14 -0
- metadata +94 -14
- data/test/sass/templates/bork.sass +0 -2
data/lib/sass/environment.rb
CHANGED
@@ -43,10 +43,16 @@ module Sass
|
|
43
43
|
def inherited_hash(name)
|
44
44
|
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
45
45
|
def #{name}(name)
|
46
|
-
|
46
|
+
_#{name}(name.gsub('_', '-'))
|
47
47
|
end
|
48
48
|
|
49
|
+
def _#{name}(name)
|
50
|
+
@#{name}s[name] || @parent && @parent._#{name}(name)
|
51
|
+
end
|
52
|
+
protected :_#{name}
|
53
|
+
|
49
54
|
def set_#{name}(name, value)
|
55
|
+
name = name.gsub('_', '-')
|
50
56
|
@#{name}s[name] = value unless try_set_#{name}(name, value)
|
51
57
|
end
|
52
58
|
|
@@ -63,7 +69,7 @@ module Sass
|
|
63
69
|
protected :try_set_#{name}
|
64
70
|
|
65
71
|
def set_local_#{name}(name, value)
|
66
|
-
@#{name}s[name] = value
|
72
|
+
@#{name}s[name.gsub('_', '-')] = value
|
67
73
|
end
|
68
74
|
RUBY
|
69
75
|
end
|
data/lib/sass/error.rb
CHANGED
@@ -4,51 +4,189 @@ module Sass
|
|
4
4
|
# and the Sass file that was being parsed (if applicable).
|
5
5
|
#
|
6
6
|
# All Sass errors are raised as {Sass::SyntaxError}s.
|
7
|
+
#
|
8
|
+
# When dealing with SyntaxErrors,
|
9
|
+
# it's important to provide filename and line number information.
|
10
|
+
# This will be used in various error reports to users, including backtraces;
|
11
|
+
# see \{#sass\_backtrace} for details.
|
12
|
+
#
|
13
|
+
# Some of this information is usually provided as part of the constructor.
|
14
|
+
# New backtrace entries can be added with \{#add\_backtrace},
|
15
|
+
# which is called when an exception is raised between files (e.g. with `@import`).
|
16
|
+
#
|
17
|
+
# Often, a chunk of code will all have similar backtrace information -
|
18
|
+
# the same filename or even line.
|
19
|
+
# It may also be useful to have a default line number set.
|
20
|
+
# In those situations, the default values can be used
|
21
|
+
# by omitting the information on the original exception,
|
22
|
+
# and then calling \{#modify\_backtrace} in a wrapper `rescue`.
|
23
|
+
# When doing this, be sure that all exceptions ultimately end up
|
24
|
+
# with the information filled in.
|
7
25
|
class SyntaxError < StandardError
|
8
|
-
# The
|
26
|
+
# The backtrace of the error within Sass files.
|
27
|
+
# This is an array of hashes containing information for a single entry.
|
28
|
+
# The hashes have the following keys:
|
9
29
|
#
|
10
|
-
#
|
11
|
-
|
30
|
+
# `:filename`
|
31
|
+
# : The name of the file in which the exception was raised,
|
32
|
+
# or `nil` if no filename is available.
|
33
|
+
#
|
34
|
+
# `:mixin`
|
35
|
+
# : The name of the mixin in which the exception was raised,
|
36
|
+
# or `nil` if it wasn't raised in a mixin.
|
37
|
+
#
|
38
|
+
# `:line`
|
39
|
+
# : The line of the file on which the error occurred. Never nil.
|
40
|
+
#
|
41
|
+
# This information is also included in standard backtrace format
|
42
|
+
# in the output of \{#backtrace}.
|
43
|
+
#
|
44
|
+
# @return [Aray<{Symbol => Object>}]
|
45
|
+
attr_accessor :sass_backtrace
|
12
46
|
|
13
|
-
# The
|
14
|
-
# This could be `nil` if no filename is available.
|
47
|
+
# The text of the template where this error was raised.
|
15
48
|
#
|
16
49
|
# @return [String]
|
17
|
-
|
50
|
+
attr_accessor :sass_template
|
18
51
|
|
19
52
|
# @param msg [String] The error message
|
20
|
-
# @param
|
21
|
-
|
53
|
+
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
|
54
|
+
# See \{#sass\_backtrace}
|
55
|
+
def initialize(msg, attrs = {})
|
22
56
|
@message = msg
|
23
|
-
@
|
57
|
+
@sass_backtrace = []
|
58
|
+
add_backtrace(attrs)
|
59
|
+
end
|
60
|
+
|
61
|
+
# The name of the file in which the exception was raised.
|
62
|
+
# This could be `nil` if no filename is available.
|
63
|
+
#
|
64
|
+
# @return [String, nil]
|
65
|
+
def sass_filename
|
66
|
+
sass_backtrace.first[:filename]
|
67
|
+
end
|
68
|
+
|
69
|
+
# The name of the mixin in which the error occurred.
|
70
|
+
# This could be `nil` if the error occurred outside a mixin.
|
71
|
+
#
|
72
|
+
# @return [Fixnum]
|
73
|
+
def sass_mixin
|
74
|
+
sass_backtrace.first[:mixin]
|
75
|
+
end
|
76
|
+
|
77
|
+
# The line of the Sass template on which the error occurred.
|
78
|
+
#
|
79
|
+
# @return [Fixnum]
|
80
|
+
def sass_line
|
81
|
+
sass_backtrace.first[:line]
|
24
82
|
end
|
25
83
|
|
26
|
-
#
|
27
|
-
# and re-raises the exception.
|
84
|
+
# Adds an entry to the exception's Sass backtrace.
|
28
85
|
#
|
29
|
-
# @param
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
self.sass_line ||= line
|
34
|
-
add_backtrace_entry(filename) unless sass_filename
|
35
|
-
raise self
|
86
|
+
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
|
87
|
+
# See \{#sass\_backtrace}
|
88
|
+
def add_backtrace(attrs)
|
89
|
+
sass_backtrace << attrs.reject {|k, v| v.nil?}
|
36
90
|
end
|
37
91
|
|
38
|
-
#
|
92
|
+
# Modify the top Sass backtrace entries
|
93
|
+
# (that is, the most deeply nested ones)
|
94
|
+
# to have the given attributes.
|
39
95
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
96
|
+
# Specifically, this goes through the backtrace entries
|
97
|
+
# from most deeply nested to least,
|
98
|
+
# setting the given attributes for each entry.
|
99
|
+
# If an entry already has one of the given attributes set,
|
100
|
+
# the pre-existing attribute takes precedence
|
101
|
+
# and is not used for less deeply-nested entries
|
102
|
+
# (even if they don't have that attribute set).
|
103
|
+
#
|
104
|
+
# @param attrs [{Symbol => Object}] The information to add to the backtrace entry.
|
105
|
+
# See \{#sass\_backtrace}
|
106
|
+
def modify_backtrace(attrs)
|
107
|
+
attrs = attrs.reject {|k, v| v.nil?}
|
108
|
+
# Move backwards through the backtrace
|
109
|
+
(0...sass_backtrace.size).to_a.reverse.each do |i|
|
110
|
+
entry = sass_backtrace[i]
|
111
|
+
sass_backtrace[i] = attrs.merge(entry)
|
112
|
+
attrs.reject! {|k, v| entry.include?(k)}
|
113
|
+
break if attrs.empty?
|
114
|
+
end
|
46
115
|
end
|
47
116
|
|
48
117
|
# @return [String] The error message
|
49
118
|
def to_s
|
50
119
|
@message
|
51
120
|
end
|
121
|
+
|
122
|
+
# Returns the standard exception backtrace,
|
123
|
+
# including the Sass backtrace.
|
124
|
+
#
|
125
|
+
# @return [Array<String>]
|
126
|
+
def backtrace
|
127
|
+
return nil if super.nil?
|
128
|
+
sass_backtrace.map do |h|
|
129
|
+
"#{h[:filename] || "(sass)"}:#{h[:line]}" +
|
130
|
+
(h[:mixin] ? ":in `#{h[:mixin]}'" : "")
|
131
|
+
end + super
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a string representation of the Sass backtrace.
|
135
|
+
#
|
136
|
+
# @param default_filename [String] The filename to use for unknown files
|
137
|
+
# @see #sass_backtrace
|
138
|
+
# @return [String]
|
139
|
+
def sass_backtrace_str(default_filename = "an unknown file")
|
140
|
+
"Syntax error: #{message}" +
|
141
|
+
Haml::Util.enum_with_index(sass_backtrace).map do |entry, i|
|
142
|
+
"\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" +
|
143
|
+
" of #{entry[:filename] || default_filename}" +
|
144
|
+
(entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
|
145
|
+
end.join
|
146
|
+
end
|
147
|
+
|
148
|
+
class << self
|
149
|
+
# Returns an error report for an exception in CSS format.
|
150
|
+
#
|
151
|
+
# @param e [Exception]
|
152
|
+
# @param options [{Symbol => Object}] The options passed to {Sass::Engine#initialize}
|
153
|
+
# @return [String] The error report
|
154
|
+
# @raise [Exception] `e`, if the
|
155
|
+
# {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
|
156
|
+
# is set to false.
|
157
|
+
def exception_to_css(e, options)
|
158
|
+
raise e unless options[:full_exception]
|
159
|
+
|
160
|
+
header = header_string(e, options)
|
161
|
+
|
162
|
+
<<END
|
163
|
+
/*
|
164
|
+
#{header}
|
165
|
+
|
166
|
+
Backtrace:\n#{e.backtrace.join("\n")}
|
167
|
+
*/
|
168
|
+
body:before {
|
169
|
+
white-space: pre;
|
170
|
+
font-family: monospace;
|
171
|
+
content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; }
|
172
|
+
END
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def header_string(e, options)
|
178
|
+
return "#{e.class}: #{e.message}" unless e.is_a? Sass::SyntaxError
|
179
|
+
|
180
|
+
line_offset = options[:line] || 1
|
181
|
+
line_num = e.sass_line + 1 - line_offset
|
182
|
+
min = [line_num - 6, 0].max
|
183
|
+
section = e.sass_template.rstrip.split("\n")[min ... line_num + 5]
|
184
|
+
return e.sass_backtrace_str if section.nil? || section.empty?
|
185
|
+
|
186
|
+
e.sass_backtrace_str + "\n\n" + Haml::Util.enum_with_index(section).
|
187
|
+
map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
|
188
|
+
end
|
189
|
+
end
|
52
190
|
end
|
53
191
|
|
54
192
|
# The class for Sass errors that are raised due to invalid unit conversions
|
data/lib/sass/files.rb
CHANGED
@@ -11,10 +11,11 @@ module Sass
|
|
11
11
|
# Returns the {Sass::Tree} for the given file,
|
12
12
|
# reading it from the Sass cache if possible.
|
13
13
|
#
|
14
|
-
# @param filename [String] The path to the Sass file
|
14
|
+
# @param filename [String] The path to the Sass or SCSS file
|
15
15
|
# @param options [{Symbol => Object}] The options hash.
|
16
16
|
# Only the {file:SASS_REFERENCE.md#cache-option `:cache_location`} option is used
|
17
|
-
# @raise [Sass::SyntaxError] if there's an error in the document
|
17
|
+
# @raise [Sass::SyntaxError] if there's an error in the document.
|
18
|
+
# The caller has responsibility for setting backtrace information, if necessary
|
18
19
|
def tree_for(filename, options)
|
19
20
|
options = Sass::Engine::DEFAULT_OPTIONS.merge(options)
|
20
21
|
text = File.read(filename)
|
@@ -29,57 +30,59 @@ module Sass
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
err.add_backtrace_entry(filename)
|
38
|
-
raise err
|
33
|
+
options = options.merge(:filename => filename)
|
34
|
+
if filename =~ /\.scss$/
|
35
|
+
options = options.merge(:syntax => :scss)
|
36
|
+
elsif filename =~ /\.sass$/
|
37
|
+
options = options.merge(:syntax => :sass)
|
39
38
|
end
|
40
39
|
|
41
|
-
|
40
|
+
engine = Sass::Engine.new(text, options)
|
42
41
|
|
42
|
+
root = engine.to_tree
|
43
|
+
try_to_write_sassc(root, compiled_filename, sha, options) if options[:cache]
|
43
44
|
root
|
44
45
|
end
|
45
46
|
|
46
|
-
# Find the full filename of a Sass or CSS file to import.
|
47
|
+
# Find the full filename of a Sass, SCSS, or CSS file to import.
|
47
48
|
# This follows Sass's import rules:
|
48
|
-
# if the filename given ends in `".sass"` or `".css"`,
|
49
|
+
# if the filename given ends in `".sass"`, `".scss"`, or `".css"`,
|
49
50
|
# it will try to find that type of file;
|
50
|
-
# otherwise, it will try to find the corresponding Sass file
|
51
|
+
# otherwise, it will try to find the corresponding Sass/SCSS file
|
51
52
|
# and fall back on CSS if it's not available.
|
52
53
|
#
|
53
|
-
# Any Sass filename returned will correspond to
|
54
|
-
# an actual
|
54
|
+
# Any Sass/SCSS filename returned will correspond to
|
55
|
+
# an actual file of the corresponding type on the filesystem.
|
55
56
|
# CSS filenames, however, may not;
|
56
57
|
# they're expected to be put through directly to the stylesheet
|
57
58
|
# as CSS `@import` statements.
|
58
59
|
#
|
59
60
|
# @param filename [String] The filename to search for
|
60
61
|
# @param load_paths [Array<String>] The set of filesystem paths
|
61
|
-
# to search for Sass files.
|
62
|
+
# to search for Sass/SCSS files.
|
62
63
|
# @return [String] The filename of the imported file.
|
63
|
-
# This is an absolute path if the file is a `".sass"` file.
|
64
|
-
# @raise [Sass::SyntaxError] if `filename` ends in
|
65
|
-
# and no corresponding Sass file could be found.
|
64
|
+
# This is an absolute path if the file is a `".sass"` or `".scss"` file.
|
65
|
+
# @raise [Sass::SyntaxError] if `filename` ends in `".sass"` or `".scss"`
|
66
|
+
# and no corresponding Sass/SCSS file could be found.
|
66
67
|
def find_file_to_import(filename, load_paths)
|
67
|
-
was_sass = false
|
68
|
+
was_sass = was_scss = false
|
68
69
|
original_filename = filename
|
69
70
|
|
70
|
-
if filename[-5..-1]
|
71
|
+
if [".sass", ".scss"].include?(filename[-5..-1])
|
72
|
+
was_sass = filename[-5..-1] == ".sass"
|
73
|
+
was_scss = filename[-5..-1] == ".scss"
|
71
74
|
filename = filename[0...-5]
|
72
|
-
was_sass = true
|
73
75
|
elsif filename[-4..-1] == ".css"
|
74
76
|
return filename
|
75
77
|
end
|
76
78
|
|
77
|
-
new_filename
|
79
|
+
new_filename = find_full_path("#{filename}.sass", load_paths) unless was_scss
|
80
|
+
new_filename ||= find_full_path("#{filename}.scss", load_paths) unless was_sass
|
78
81
|
|
79
82
|
return new_filename if new_filename
|
80
|
-
unless was_sass
|
81
|
-
|
82
|
-
WARNING: #{filename}.sass
|
83
|
+
unless was_sass || was_scss
|
84
|
+
Haml::Util.haml_warn <<END
|
85
|
+
WARNING: Neither #{filename}.sass nor .scss found. Using #{filename}.css instead.
|
83
86
|
This behavior is deprecated and will be removed in a future version.
|
84
87
|
If you really need #{filename}.css, import it explicitly.
|
85
88
|
END
|
@@ -93,7 +96,7 @@ END
|
|
93
96
|
message << "Load paths:\n " << load_paths.join("\n ")
|
94
97
|
end
|
95
98
|
|
96
|
-
raise SyntaxError.new(message
|
99
|
+
raise SyntaxError.new(message)
|
97
100
|
end
|
98
101
|
|
99
102
|
private
|
@@ -113,7 +116,7 @@ END
|
|
113
116
|
return Marshal.load(f.read)
|
114
117
|
end
|
115
118
|
rescue EOFError, TypeError, ArgumentError => e
|
116
|
-
|
119
|
+
Haml::Util.haml_warn "Warning. Error encountered while reading cache #{compiled_filename}: #{e}"
|
117
120
|
end
|
118
121
|
|
119
122
|
def try_to_write_sassc(root, compiled_filename, sha, options)
|
data/lib/sass/plugin.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
require '
|
1
|
+
require 'fileutils'
|
2
2
|
require 'rbconfig'
|
3
3
|
|
4
|
+
require 'sass'
|
5
|
+
require 'sass/callbacks'
|
6
|
+
|
4
7
|
module Sass
|
5
|
-
# This module handles the compilation of Sass files.
|
8
|
+
# This module handles the compilation of Sass/SCSS files.
|
6
9
|
# It provides global options and checks whether CSS files
|
7
10
|
# need to be updated.
|
8
11
|
#
|
@@ -11,7 +14,23 @@ module Sass
|
|
11
14
|
# All Rack-enabled frameworks are supported out of the box.
|
12
15
|
# The plugin is {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
|
13
16
|
# Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
|
17
|
+
#
|
18
|
+
# This module has a large set of callbacks available
|
19
|
+
# to allow users to run code (such as logging) when certain things happen.
|
20
|
+
# All callback methods are of the form `on_#{name}`,
|
21
|
+
# and they all take a block that's called when the given action occurs.
|
22
|
+
#
|
23
|
+
# @example Using a callback
|
24
|
+
# Sass::Plugin.on_updating_stylesheet do |template, css|
|
25
|
+
# puts "Compiling #{template} to #{css}"
|
26
|
+
# end
|
27
|
+
# Sass::Plugin.update_stylesheets
|
28
|
+
# #=> Compiling app/sass/screen.scss to public/stylesheets/screen.css
|
29
|
+
# #=> Compiling app/sass/print.scss to public/stylesheets/print.css
|
30
|
+
# #=> Compiling app/sass/ie.scss to public/stylesheets/ie.css
|
14
31
|
module Plugin
|
32
|
+
include Haml::Util
|
33
|
+
include Sass::Callbacks
|
15
34
|
extend self
|
16
35
|
|
17
36
|
@options = {
|
@@ -22,6 +41,114 @@ module Sass
|
|
22
41
|
}
|
23
42
|
@checked_for_updates = false
|
24
43
|
|
44
|
+
# Register a callback to be run before stylesheets are mass-updated.
|
45
|
+
# This is run whenever \{#update\_stylesheets} is called,
|
46
|
+
# unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
|
47
|
+
# is enabled.
|
48
|
+
#
|
49
|
+
# @yield [individual_files]
|
50
|
+
# @yieldparam individual_files [<(String, String)>]
|
51
|
+
# Individual files to be updated, in addition to the directories
|
52
|
+
# specified in the options.
|
53
|
+
# The first element of each pair is the source file,
|
54
|
+
# the second is the target CSS file.
|
55
|
+
define_callback :updating_stylesheets
|
56
|
+
|
57
|
+
# Register a callback to be run before a single stylesheet is updated.
|
58
|
+
# The callback is only run if the stylesheet is guaranteed to be updated;
|
59
|
+
# if the CSS file is fresh, this won't be run.
|
60
|
+
#
|
61
|
+
# Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
|
62
|
+
# is enabled, this callback won't be run
|
63
|
+
# when an exception CSS file is being written.
|
64
|
+
# To run an action for those files, use \{#on\_compilation\_error}.
|
65
|
+
#
|
66
|
+
# @yield [template, css]
|
67
|
+
# @yieldparam template [String]
|
68
|
+
# The location of the Sass/SCSS file being updated.
|
69
|
+
# @yieldparam css [String]
|
70
|
+
# The location of the CSS file being generated.
|
71
|
+
define_callback :updating_stylesheet
|
72
|
+
|
73
|
+
# Register a callback to be run when Sass decides not to update a stylesheet.
|
74
|
+
# In particular, the callback is run when Sass finds that
|
75
|
+
# the template file and none of its dependencies
|
76
|
+
# have been modified since the last compilation.
|
77
|
+
#
|
78
|
+
# Note that this is **not** run when the
|
79
|
+
# \{file:SASS_REFERENCE.md#never-update_option `:never_update` option} is set,
|
80
|
+
# nor when Sass decides not to compile a partial.
|
81
|
+
#
|
82
|
+
# @yield [template, css]
|
83
|
+
# @yieldparam template [String]
|
84
|
+
# The location of the Sass/SCSS file not being updated.
|
85
|
+
# @yieldparam css [String]
|
86
|
+
# The location of the CSS file not being generated.
|
87
|
+
define_callback :not_updating_stylesheet
|
88
|
+
|
89
|
+
# Register a callback to be run when there's an error
|
90
|
+
# compiling a Sass file.
|
91
|
+
# This could include not only errors in the Sass document,
|
92
|
+
# but also errors accessing the file at all.
|
93
|
+
#
|
94
|
+
# @yield [error, template, css]
|
95
|
+
# @yieldparam error [Exception] The exception that was raised.
|
96
|
+
# @yieldparam template [String]
|
97
|
+
# The location of the Sass/SCSS file being updated.
|
98
|
+
# @yieldparam css [String]
|
99
|
+
# The location of the CSS file being generated.
|
100
|
+
define_callback :compilation_error
|
101
|
+
|
102
|
+
# Register a callback to be run when Sass creates a directory
|
103
|
+
# into which to put CSS files.
|
104
|
+
#
|
105
|
+
# Note that even if multiple levels of directories need to be created,
|
106
|
+
# the callback may only be run once.
|
107
|
+
# For example, if "foo/" exists and "foo/bar/baz/" needs to be created,
|
108
|
+
# this may only be run for "foo/bar/baz/".
|
109
|
+
# This is not a guarantee, however;
|
110
|
+
# it may also be run for "foo/bar/".
|
111
|
+
#
|
112
|
+
# @yield [dirname]
|
113
|
+
# @yieldparam dirname [String]
|
114
|
+
# The location of the directory that was created.
|
115
|
+
define_callback :creating_directory
|
116
|
+
|
117
|
+
# Register a callback to be run when Sass detects
|
118
|
+
# that a template has been modified.
|
119
|
+
# This is only run when using \{#watch}.
|
120
|
+
#
|
121
|
+
# @yield [template]
|
122
|
+
# @yieldparam template [String]
|
123
|
+
# The location of the template that was modified.
|
124
|
+
define_callback :template_modified
|
125
|
+
|
126
|
+
# Register a callback to be run when Sass detects
|
127
|
+
# that a new template has been created.
|
128
|
+
# This is only run when using \{#watch}.
|
129
|
+
#
|
130
|
+
# @yield [template]
|
131
|
+
# @yieldparam template [String]
|
132
|
+
# The location of the template that was created.
|
133
|
+
define_callback :template_created
|
134
|
+
|
135
|
+
# Register a callback to be run when Sass detects
|
136
|
+
# that a template has been deleted.
|
137
|
+
# This is only run when using \{#watch}.
|
138
|
+
#
|
139
|
+
# @yield [template]
|
140
|
+
# @yieldparam template [String]
|
141
|
+
# The location of the template that was deleted.
|
142
|
+
define_callback :template_deleted
|
143
|
+
|
144
|
+
# Register a callback to be run when Sass deletes a CSS file.
|
145
|
+
# This happens when the corresponding Sass/SCSS file has been deleted.
|
146
|
+
#
|
147
|
+
# @yield [filename]
|
148
|
+
# @yieldparam filename [String]
|
149
|
+
# The location of the CSS file that was deleted.
|
150
|
+
define_callback :deleting_css
|
151
|
+
|
25
152
|
# Whether or not Sass has **ever** checked if the stylesheets need to be updated
|
26
153
|
# (in this Ruby instance).
|
27
154
|
#
|
@@ -65,61 +192,163 @@ module Sass
|
|
65
192
|
|
66
193
|
# Updates out-of-date stylesheets.
|
67
194
|
#
|
68
|
-
# Checks each Sass file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
195
|
+
# Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
|
69
196
|
# to see if it's been modified more recently than the corresponding CSS file
|
70
197
|
# in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
|
71
198
|
# If it has, it updates the CSS file.
|
72
|
-
|
199
|
+
#
|
200
|
+
# @param individual_files [Array<(String, String)>]
|
201
|
+
# A list of files to check for updates
|
202
|
+
# **in addition to those specified by the
|
203
|
+
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
204
|
+
# The first string in each pair is the location of the Sass/SCSS file,
|
205
|
+
# the second is the location of the CSS file that it should be compiled to.
|
206
|
+
def update_stylesheets(individual_files = [])
|
73
207
|
return if options[:never_update]
|
74
208
|
|
209
|
+
run_updating_stylesheets individual_files
|
210
|
+
|
211
|
+
individual_files.each {|t, c| update_stylesheet(t, c)}
|
212
|
+
|
75
213
|
@checked_for_updates = true
|
76
214
|
template_locations.zip(css_locations).each do |template_location, css_location|
|
77
215
|
|
78
|
-
Dir.glob(File.join(template_location, "**", "*.
|
79
|
-
# Get the relative path to the file
|
80
|
-
name = file.sub(template_location.sub(/\/*$/, '/'), "")
|
216
|
+
Dir.glob(File.join(template_location, "**", "*.s[ca]ss")).each do |file|
|
217
|
+
# Get the relative path to the file
|
218
|
+
name = file.sub(template_location.sub(/\/*$/, '/'), "")
|
219
|
+
css = css_filename(name, css_location)
|
81
220
|
|
82
|
-
if
|
83
|
-
|
221
|
+
next if forbid_update?(name)
|
222
|
+
if options[:always_update] || stylesheet_needs_update?(css, file)
|
223
|
+
update_stylesheet file, css
|
224
|
+
else
|
225
|
+
run_not_updating_stylesheet file, css
|
84
226
|
end
|
85
227
|
end
|
86
228
|
end
|
87
229
|
end
|
88
230
|
|
89
|
-
|
231
|
+
# Watches the template directory (or directories)
|
232
|
+
# and updates the CSS files whenever the related Sass/SCSS files change.
|
233
|
+
# `watch` never returns.
|
234
|
+
#
|
235
|
+
# Whenever a change is detected to a Sass/SCSS file in
|
236
|
+
# {file:SASS_REFERENCE.md#template_location-option `:template_location`},
|
237
|
+
# the corresponding CSS file in {file:SASS_REFERENCE.md#css_location-option `:css_location`}
|
238
|
+
# will be recompiled.
|
239
|
+
# The CSS files of any Sass/SCSS files that import the changed file will also be recompiled.
|
240
|
+
#
|
241
|
+
# Before the watching starts in earnest, `watch` calls \{#update\_stylesheets}.
|
242
|
+
#
|
243
|
+
# Note that `watch` uses the [FSSM](http://github.com/ttilley/fssm) library
|
244
|
+
# to monitor the filesystem for changes.
|
245
|
+
# FSSM isn't loaded until `watch` is run.
|
246
|
+
# The version of FSSM distributed with Sass is loaded by default,
|
247
|
+
# but if another version has already been loaded that will be used instead.
|
248
|
+
#
|
249
|
+
# @param individual_files [Array<(String, String)>]
|
250
|
+
# A list of files to watch for updates
|
251
|
+
# **in addition to those specified by the
|
252
|
+
# {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
|
253
|
+
# The first string in each pair is the location of the Sass/SCSS file,
|
254
|
+
# the second is the location of the CSS file that it should be compiled to.
|
255
|
+
def watch(individual_files = [])
|
256
|
+
update_stylesheets(individual_files)
|
257
|
+
|
258
|
+
begin
|
259
|
+
require 'fssm'
|
260
|
+
rescue LoadError => e
|
261
|
+
e.message << "\n" <<
|
262
|
+
if File.exists?(scope(".git"))
|
263
|
+
'Run "git submodule update --init" to get the recommended version.'
|
264
|
+
else
|
265
|
+
'Run "gem install fssm" to get it.'
|
266
|
+
end
|
267
|
+
raise e
|
268
|
+
end
|
269
|
+
|
270
|
+
# TODO: Keep better track of what depends on what
|
271
|
+
# so we don't have to run a global update every time anything changes.
|
272
|
+
FSSM.monitor do |mon|
|
273
|
+
template_locations.zip(css_locations).each do |template_location, css_location|
|
274
|
+
mon.path template_location do |path|
|
275
|
+
path.glob '**/*.s[ac]ss'
|
276
|
+
|
277
|
+
path.update do |base, relative|
|
278
|
+
run_template_modified File.join(base, relative)
|
279
|
+
update_stylesheets(individual_files)
|
280
|
+
end
|
281
|
+
|
282
|
+
path.create do |base, relative|
|
283
|
+
run_template_created File.join(base, relative)
|
284
|
+
update_stylesheets(individual_files)
|
285
|
+
end
|
286
|
+
|
287
|
+
path.delete do |base, relative|
|
288
|
+
run_template_deleted File.join(base, relative)
|
289
|
+
css = File.join(css_location, relative.gsub(/\.s[ac]ss$/, '.css'))
|
290
|
+
try_delete_css css
|
291
|
+
update_stylesheets(individual_files)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
individual_files.each do |template, css|
|
297
|
+
mon.file template do |path|
|
298
|
+
path.update do
|
299
|
+
run_template_modified template
|
300
|
+
update_stylesheets(individual_files)
|
301
|
+
end
|
302
|
+
|
303
|
+
path.create do
|
304
|
+
run_template_created template
|
305
|
+
update_stylesheets(individual_files)
|
306
|
+
end
|
307
|
+
|
308
|
+
path.delete do
|
309
|
+
run_template_deleted template
|
310
|
+
try_delete_css css
|
311
|
+
update_stylesheets(individual_files)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
90
317
|
|
91
|
-
|
92
|
-
css = css_filename(name, css_location)
|
93
|
-
File.delete(css) if File.exists?(css)
|
318
|
+
private
|
94
319
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
320
|
+
def update_stylesheet(filename, css)
|
321
|
+
dir = File.dirname(css)
|
322
|
+
unless File.exists?(dir)
|
323
|
+
run_creating_directory dir
|
324
|
+
FileUtils.mkdir_p dir
|
325
|
+
end
|
102
326
|
|
103
|
-
|
104
|
-
|
327
|
+
begin
|
328
|
+
result = Sass::Files.tree_for(filename, engine_options(:css_filename => css, :filename => filename)).render
|
329
|
+
rescue Exception => e
|
330
|
+
run_compilation_error e, filename, css
|
331
|
+
result = Sass::SyntaxError.exception_to_css(e, options)
|
332
|
+
else
|
333
|
+
run_updating_stylesheet filename, css
|
334
|
+
end
|
105
335
|
|
106
336
|
# Finally, write the file
|
107
337
|
flag = 'w'
|
108
338
|
flag = 'wb' if RbConfig::CONFIG['host_os'] =~ /mswin|windows/i && options[:unix_newlines]
|
109
339
|
File.open(css, flag) {|file| file.print(result)}
|
110
340
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
dirs.each { |dir| Dir.mkdir(dir) unless File.exist?(dir) }
|
341
|
+
|
342
|
+
def try_delete_css(css)
|
343
|
+
return unless File.exists?(css)
|
344
|
+
run_deleting_css css
|
345
|
+
File.delete css
|
117
346
|
end
|
118
347
|
|
119
348
|
def load_paths(opts = options)
|
120
349
|
(opts[:load_paths] || []) + template_locations
|
121
350
|
end
|
122
|
-
|
351
|
+
|
123
352
|
def template_locations
|
124
353
|
location = (options[:template_location] || File.join(options[:css_location],'sass'))
|
125
354
|
if location.is_a?(String)
|
@@ -128,7 +357,7 @@ module Sass
|
|
128
357
|
location.to_a.map { |l| l.first }
|
129
358
|
end
|
130
359
|
end
|
131
|
-
|
360
|
+
|
132
361
|
def css_locations
|
133
362
|
if options[:template_location] && !options[:template_location].is_a?(String)
|
134
363
|
options[:template_location].to_a.map { |l| l.last }
|
@@ -137,65 +366,16 @@ module Sass
|
|
137
366
|
end
|
138
367
|
end
|
139
368
|
|
140
|
-
def exception_string(e)
|
141
|
-
e_string = "#{e.class}: #{e.message}"
|
142
|
-
|
143
|
-
if e.is_a? Sass::SyntaxError
|
144
|
-
e_string << "\non line #{e.sass_line}"
|
145
|
-
|
146
|
-
if e.sass_filename
|
147
|
-
e_string << " of #{e.sass_filename}"
|
148
|
-
|
149
|
-
if File.exists?(e.sass_filename)
|
150
|
-
e_string << "\n\n"
|
151
|
-
|
152
|
-
min = [e.sass_line - 5, 0].max
|
153
|
-
begin
|
154
|
-
File.read(e.sass_filename).rstrip.split("\n")[
|
155
|
-
min .. e.sass_line + 5
|
156
|
-
].each_with_index do |line, i|
|
157
|
-
e_string << "#{min + i + 1}: #{line}\n"
|
158
|
-
end
|
159
|
-
rescue
|
160
|
-
e_string << "Couldn't read sass file: #{e.sass_filename}"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
<<END
|
166
|
-
/*
|
167
|
-
#{e_string}
|
168
|
-
|
169
|
-
Backtrace:\n#{e.backtrace.join("\n")}
|
170
|
-
*/
|
171
|
-
body:before {
|
172
|
-
white-space: pre;
|
173
|
-
font-family: monospace;
|
174
|
-
content: "#{e_string.gsub('"', '\"').gsub("\n", '\\A ')}"; }
|
175
|
-
END
|
176
|
-
# Fix an emacs syntax-highlighting hiccup: '
|
177
|
-
end
|
178
|
-
|
179
|
-
def template_filename(name, path)
|
180
|
-
"#{path}/#{name}.sass"
|
181
|
-
end
|
182
|
-
|
183
369
|
def css_filename(name, path)
|
184
|
-
"#{path}/#{name}.css
|
370
|
+
"#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
|
185
371
|
end
|
186
372
|
|
187
373
|
def forbid_update?(name)
|
188
374
|
name.sub(/^.*\//, '')[0] == ?_
|
189
375
|
end
|
190
376
|
|
191
|
-
def stylesheet_needs_update?(
|
192
|
-
css_file
|
193
|
-
template_file = template_filename(name, template_path)
|
194
|
-
exact_stylesheet_needs_update?(css_file, template_file)
|
195
|
-
end
|
196
|
-
|
197
|
-
def exact_stylesheet_needs_update?(css_file, template_file)
|
198
|
-
return true unless File.exists?(css_file)
|
377
|
+
def stylesheet_needs_update?(css_file, template_file)
|
378
|
+
return true unless File.exists?(css_file) && File.exists?(template_file)
|
199
379
|
|
200
380
|
css_mtime = File.mtime(css_file)
|
201
381
|
File.mtime(template_file) > css_mtime ||
|
@@ -215,11 +395,12 @@ END
|
|
215
395
|
end
|
216
396
|
|
217
397
|
def dependencies(filename)
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
398
|
+
Files.tree_for(filename, engine_options).select {|n| n.is_a?(Tree::ImportNode)}.map do |n|
|
399
|
+
next if n.full_filename =~ /\.css$/
|
400
|
+
n.full_filename
|
401
|
+
end.compact
|
402
|
+
rescue Sass::SyntaxError => e
|
403
|
+
[] # If the file has an error, we assume it has no dependencies
|
223
404
|
end
|
224
405
|
end
|
225
406
|
end
|