haml 1.5.2 → 1.7.0
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/MIT-LICENSE +1 -1
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/css2sass +7 -0
- data/bin/html2haml +0 -82
- data/lib/haml.rb +43 -6
- data/lib/haml/buffer.rb +81 -72
- data/lib/haml/engine.rb +240 -110
- data/lib/haml/exec.rb +120 -5
- data/lib/haml/helpers.rb +88 -3
- data/lib/haml/helpers/action_view_extensions.rb +45 -0
- data/lib/haml/helpers/action_view_mods.rb +30 -17
- data/lib/haml/html.rb +173 -0
- data/lib/haml/template.rb +1 -26
- data/lib/haml/util.rb +18 -0
- data/lib/sass.rb +181 -3
- data/lib/sass/constant.rb +38 -9
- data/lib/sass/constant/color.rb +25 -1
- data/lib/sass/constant/literal.rb +10 -8
- data/lib/sass/css.rb +197 -0
- data/lib/sass/engine.rb +239 -68
- data/lib/sass/error.rb +2 -2
- data/lib/sass/plugin.rb +11 -3
- data/lib/sass/tree/attr_node.rb +25 -17
- data/lib/sass/tree/comment_node.rb +14 -0
- data/lib/sass/tree/node.rb +18 -1
- data/lib/sass/tree/rule_node.rb +17 -5
- data/lib/sass/tree/value_node.rb +4 -0
- data/test/haml/engine_test.rb +42 -25
- data/test/haml/helper_test.rb +28 -3
- data/test/haml/results/eval_suppressed.xhtml +6 -0
- data/test/haml/results/helpers.xhtml +26 -2
- data/test/haml/results/helpful.xhtml +2 -0
- data/test/haml/results/just_stuff.xhtml +17 -2
- data/test/haml/results/standard.xhtml +1 -1
- data/test/haml/results/whitespace_handling.xhtml +1 -11
- data/test/haml/rhtml/standard.rhtml +1 -1
- data/test/haml/template_test.rb +7 -2
- data/test/haml/templates/eval_suppressed.haml +7 -2
- data/test/haml/templates/helpers.haml +16 -1
- data/test/haml/templates/helpful.haml +2 -0
- data/test/haml/templates/just_stuff.haml +23 -4
- data/test/haml/templates/standard.haml +3 -3
- data/test/haml/templates/whitespace_handling.haml +0 -50
- data/test/sass/engine_test.rb +35 -10
- data/test/sass/plugin_test.rb +10 -6
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/complex.css +4 -3
- data/test/sass/results/constants.css +3 -3
- data/test/sass/results/import.css +27 -0
- data/test/sass/results/nested.css +7 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +1 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/complex.sass +19 -1
- data/test/sass/templates/constants.sass +8 -0
- data/test/sass/templates/import.sass +8 -0
- data/test/sass/templates/importee.sass +10 -0
- data/test/sass/templates/nested.sass +8 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- metadata +95 -75
- data/test/haml/results/semantic.cache +0 -15
data/lib/sass/css.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../sass'
|
2
|
+
require 'sass/tree/node'
|
3
|
+
require 'strscan'
|
4
|
+
|
5
|
+
module Sass
|
6
|
+
# :stopdoc:
|
7
|
+
module Tree
|
8
|
+
class Node
|
9
|
+
def to_sass
|
10
|
+
result = ''
|
11
|
+
|
12
|
+
children.each do |child|
|
13
|
+
result << "#{child.to_sass(0)}\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ValueNode
|
21
|
+
def to_sass(tabs)
|
22
|
+
"#{value}\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class RuleNode
|
27
|
+
def to_sass(tabs)
|
28
|
+
str = "#{' ' * tabs}#{rule}\n"
|
29
|
+
|
30
|
+
children.each do |child|
|
31
|
+
str << "#{child.to_sass(tabs + 1)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
str
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class AttrNode
|
39
|
+
def to_sass(tabs)
|
40
|
+
"#{' ' * tabs}:#{name} #{value}\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# :startdoc:
|
45
|
+
|
46
|
+
# This class contains the functionality used in the +css2sass+ utility,
|
47
|
+
# namely converting CSS documents to Sass templates.
|
48
|
+
class CSS
|
49
|
+
# :stopdoc:
|
50
|
+
|
51
|
+
# The Regexp matching a CSS rule
|
52
|
+
RULE_RE = /\s*([^\{]+)\s*\{/
|
53
|
+
|
54
|
+
# The Regexp matching a CSS attribute
|
55
|
+
ATTR_RE = /\s*[^::\{\}]+\s*:\s*[^:;\{\}]+\s*;/
|
56
|
+
|
57
|
+
# :startdoc:
|
58
|
+
|
59
|
+
# Creates a new instance of Sass::CSS that will compile the given document
|
60
|
+
# to a Sass string when +render+ is called.
|
61
|
+
def initialize(template)
|
62
|
+
if template.is_a? IO
|
63
|
+
template = template.read
|
64
|
+
end
|
65
|
+
|
66
|
+
@template = StringScanner.new(template)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Processes the document and returns the result as a string
|
70
|
+
# containing the CSS template.
|
71
|
+
def render
|
72
|
+
begin
|
73
|
+
build_tree.to_sass
|
74
|
+
rescue Exception => err
|
75
|
+
line = @template.string[0...@template.pos].split("\n").size
|
76
|
+
|
77
|
+
err.backtrace.unshift "(css):#{line}"
|
78
|
+
raise err
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def build_tree
|
85
|
+
root = Tree::Node.new(nil)
|
86
|
+
whitespace
|
87
|
+
directives(root)
|
88
|
+
rules(root)
|
89
|
+
sort_rules(root)
|
90
|
+
root
|
91
|
+
end
|
92
|
+
|
93
|
+
def directives(root)
|
94
|
+
while @template.scan(/@/)
|
95
|
+
name = @template.scan /[^\s;]+/
|
96
|
+
whitespace
|
97
|
+
value = @template.scan /[^;]+/
|
98
|
+
assert_match /;/
|
99
|
+
whitespace
|
100
|
+
|
101
|
+
if name == "import" && value =~ /^(url\()?"?([^\s\(\)\"]+)\.css"?\)?$/
|
102
|
+
value = $2
|
103
|
+
end
|
104
|
+
|
105
|
+
root << Tree::ValueNode.new("@#{name} #{value};", nil)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def rules(root)
|
110
|
+
rules = []
|
111
|
+
while @template.scan(/[^\{\s]+/)
|
112
|
+
rules << @template[0]
|
113
|
+
whitespace
|
114
|
+
|
115
|
+
if @template.scan(/\{/)
|
116
|
+
result = Tree::RuleNode.new(rules.join(' '), nil)
|
117
|
+
root << result
|
118
|
+
rules = []
|
119
|
+
|
120
|
+
whitespace
|
121
|
+
attributes(result)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def attributes(rule)
|
127
|
+
while @template.scan(/[^:\}\s]+/)
|
128
|
+
name = @template[0]
|
129
|
+
whitespace
|
130
|
+
|
131
|
+
assert_match /:/
|
132
|
+
|
133
|
+
value = ''
|
134
|
+
while @template.scan(/[^;\s]+/)
|
135
|
+
value << @template[0] << whitespace
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_match /;/
|
139
|
+
rule << Tree::AttrNode.new(name, value, nil)
|
140
|
+
end
|
141
|
+
|
142
|
+
assert_match /\}/
|
143
|
+
end
|
144
|
+
|
145
|
+
def whitespace
|
146
|
+
space = @template.scan(/\s*/) || ''
|
147
|
+
|
148
|
+
# If we've hit a comment,
|
149
|
+
# go past it and look for more whitespace
|
150
|
+
if @template.scan(/\/\*/)
|
151
|
+
@template.scan_until(/\*\//)
|
152
|
+
return space + whitespace
|
153
|
+
end
|
154
|
+
return space
|
155
|
+
end
|
156
|
+
|
157
|
+
def assert_match(re)
|
158
|
+
if !@template.scan(re)
|
159
|
+
raise Exception.new("Invalid CSS!")
|
160
|
+
end
|
161
|
+
whitespace
|
162
|
+
end
|
163
|
+
|
164
|
+
def sort_rules(root)
|
165
|
+
root.children.sort! do |c1, c2|
|
166
|
+
if c1.is_a?(Tree::RuleNode) && c2.is_a?(Tree::RuleNode)
|
167
|
+
c1.rule <=> c2.rule
|
168
|
+
elsif !(c1.is_a?(Tree::RuleNode) || c2.is_a?(Tree::RuleNode)) || c2.is_a?(Tree::RuleNode)
|
169
|
+
-1
|
170
|
+
else
|
171
|
+
1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
prev_rules = []
|
176
|
+
prev_rule_values = []
|
177
|
+
root.children.each do |child|
|
178
|
+
if child.is_a? Tree::RuleNode
|
179
|
+
joined_prev_values = prev_rule_values.join(' ')
|
180
|
+
until prev_rules.empty? || child.rule =~ /^#{Regexp.escape(joined_prev_values)}/
|
181
|
+
prev_rules.pop
|
182
|
+
prev_rule_values.pop
|
183
|
+
end
|
184
|
+
|
185
|
+
unless prev_rules.empty?
|
186
|
+
child.rule.slice!(0..(joined_prev_values.size))
|
187
|
+
prev_rules[-1] << child
|
188
|
+
root.children.delete child
|
189
|
+
end
|
190
|
+
|
191
|
+
prev_rules << child
|
192
|
+
prev_rule_values << child.rule
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/lib/sass/engine.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'sass/tree/node'
|
2
2
|
require 'sass/tree/value_node'
|
3
3
|
require 'sass/tree/rule_node'
|
4
|
+
require 'sass/tree/comment_node'
|
5
|
+
require 'sass/tree/attr_node'
|
4
6
|
require 'sass/constant'
|
5
7
|
require 'sass/error'
|
8
|
+
require 'haml/util'
|
6
9
|
|
7
10
|
module Sass
|
8
11
|
# This is the class where all the parsing and processing of the Sass
|
@@ -16,17 +19,37 @@ module Sass
|
|
16
19
|
class Engine
|
17
20
|
# The character that begins a CSS attribute.
|
18
21
|
ATTRIBUTE_CHAR = ?:
|
19
|
-
|
22
|
+
|
20
23
|
# The character that designates that
|
21
24
|
# an attribute should be assigned to the result of constant arithmetic.
|
22
25
|
SCRIPT_CHAR = ?=
|
23
|
-
|
24
|
-
# The string that begins one-line comments.
|
25
|
-
COMMENT_STRING = '//'
|
26
26
|
|
27
|
-
# The
|
28
|
-
|
29
|
-
|
27
|
+
# The character that designates the beginning of a comment,
|
28
|
+
# either Sass or CSS.
|
29
|
+
COMMENT_CHAR = ?/
|
30
|
+
|
31
|
+
# The character that follows the general COMMENT_CHAR and designates a Sass comment,
|
32
|
+
# which is not output as a CSS comment.
|
33
|
+
SASS_COMMENT_CHAR = ?/
|
34
|
+
|
35
|
+
# The character that follows the general COMMENT_CHAR and designates a CSS comment,
|
36
|
+
# which is embedded in the CSS document.
|
37
|
+
CSS_COMMENT_CHAR = ?*
|
38
|
+
|
39
|
+
# The character used to denote a compiler directive.
|
40
|
+
DIRECTIVE_CHAR = ?@
|
41
|
+
|
42
|
+
# The regex that matches and extracts data from
|
43
|
+
# attributes of the form <tt>:name attr</tt>.
|
44
|
+
ATTRIBUTE = /^:([^\s=:]+)\s*(=?)(?:\s+|$)(.*)/
|
45
|
+
|
46
|
+
# The regex that matches attributes of the form <tt>name: attr</tt>.
|
47
|
+
ATTRIBUTE_ALTERNATE_MATCHER = /^[^\s:]+\s*[=:](\s|$)/
|
48
|
+
|
49
|
+
# The regex that matches and extracts data from
|
50
|
+
# attributes of the form <tt>name: attr</tt>.
|
51
|
+
ATTRIBUTE_ALTERNATE = /^([^\s=:]+)(\s*=|:)(?:\s+|$)(.*)/
|
52
|
+
|
30
53
|
# Creates a new instace of Sass::Engine that will compile the given
|
31
54
|
# template string when <tt>render</tt> is called.
|
32
55
|
# See README for available options.
|
@@ -41,139 +64,287 @@ module Sass
|
|
41
64
|
#
|
42
65
|
def initialize(template, options={})
|
43
66
|
@options = {
|
44
|
-
:style => :nested
|
67
|
+
:style => :nested,
|
68
|
+
:load_paths => ['.']
|
45
69
|
}.merge! options
|
46
|
-
@template = template.split(
|
70
|
+
@template = template.split(/\n\r|\n/)
|
47
71
|
@lines = []
|
48
72
|
@constants = {}
|
49
73
|
end
|
50
|
-
|
74
|
+
|
51
75
|
# Processes the template and returns the result as a string.
|
52
76
|
def render
|
53
77
|
begin
|
54
|
-
|
55
|
-
|
56
|
-
root = Tree::Node.new(@options[:style])
|
57
|
-
index = 0
|
58
|
-
while @lines[index]
|
59
|
-
child, index = build_tree(index)
|
60
|
-
child.line = index if child
|
61
|
-
root << child if child
|
62
|
-
end
|
63
|
-
@line = nil
|
64
|
-
|
65
|
-
root.to_s
|
78
|
+
render_to_tree.to_s
|
66
79
|
rescue SyntaxError => err
|
67
|
-
err.
|
80
|
+
unless err.sass_filename
|
81
|
+
err.add_backtrace_entry(@options[:filename])
|
82
|
+
end
|
68
83
|
raise err
|
69
84
|
end
|
70
85
|
end
|
71
86
|
|
72
87
|
alias_method :to_css, :render
|
73
|
-
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def constants
|
92
|
+
@constants
|
93
|
+
end
|
94
|
+
|
95
|
+
def render_to_tree
|
96
|
+
split_lines
|
97
|
+
|
98
|
+
root = Tree::Node.new(@options[:style])
|
99
|
+
index = 0
|
100
|
+
while @lines[index]
|
101
|
+
child, index = build_tree(index)
|
102
|
+
|
103
|
+
if child.is_a? Tree::Node
|
104
|
+
child.line = index
|
105
|
+
root << child
|
106
|
+
elsif child.is_a? Array
|
107
|
+
child.each do |c|
|
108
|
+
root << c
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
@line = nil
|
113
|
+
|
114
|
+
root
|
115
|
+
end
|
116
|
+
|
74
117
|
private
|
75
|
-
|
118
|
+
|
76
119
|
# Readies each line in the template for parsing,
|
77
120
|
# and computes the tabulation of the line.
|
78
121
|
def split_lines
|
122
|
+
@line = 0
|
79
123
|
old_tabs = 0
|
80
124
|
@template.each_with_index do |line, index|
|
81
|
-
@line
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
tabs =
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@lines << [line.strip, tabs]
|
93
|
-
|
94
|
-
old_tabs = tabs
|
125
|
+
@line += 1
|
126
|
+
|
127
|
+
tabs = count_tabs(line)
|
128
|
+
|
129
|
+
if line[0] == COMMENT_CHAR && line[1] == SASS_COMMENT_CHAR && tabs == 0
|
130
|
+
tabs = old_tabs
|
131
|
+
end
|
132
|
+
|
133
|
+
if tabs # if line isn't blank
|
134
|
+
if tabs - old_tabs > 1
|
135
|
+
raise SyntaxError.new("Illegal Indentation: Only two space characters are allowed as tabulation.", @line)
|
95
136
|
end
|
137
|
+
@lines << [line.strip, tabs]
|
138
|
+
|
139
|
+
old_tabs = tabs
|
140
|
+
else
|
141
|
+
@lines << ['//', old_tabs]
|
96
142
|
end
|
97
143
|
end
|
144
|
+
|
98
145
|
@line = nil
|
99
146
|
end
|
100
|
-
|
147
|
+
|
101
148
|
# Counts the tabulation of a line.
|
102
149
|
def count_tabs(line)
|
103
150
|
spaces = line.index(/[^ ]/)
|
104
151
|
if spaces
|
105
152
|
if spaces % 2 == 1 || line[spaces] == ?\t
|
106
|
-
|
153
|
+
# Make sure a line with just tabs isn't an error
|
154
|
+
return nil if line.strip.empty?
|
155
|
+
|
156
|
+
raise SyntaxError.new("Illegal Indentation: Only two space characters are allowed as tabulation.", @line)
|
107
157
|
end
|
108
158
|
spaces / 2
|
109
159
|
else
|
110
160
|
nil
|
111
161
|
end
|
112
162
|
end
|
113
|
-
|
163
|
+
|
114
164
|
def build_tree(index)
|
115
165
|
line, tabs = @lines[index]
|
116
166
|
index += 1
|
117
167
|
@line = index
|
118
168
|
node = parse_line(line)
|
119
169
|
|
120
|
-
# Node is nil if it's non-outputting, like a constant assignment
|
121
|
-
return nil, index unless node
|
122
|
-
|
123
170
|
has_children = has_children?(index, tabs)
|
124
|
-
|
125
|
-
while has_children
|
126
|
-
child, index = build_tree(index)
|
127
171
|
|
128
|
-
|
129
|
-
|
172
|
+
# Node is a symbol if it's non-outputting, like a constant assignment
|
173
|
+
unless node.is_a? Tree::Node
|
174
|
+
if has_children
|
175
|
+
if node == :constant
|
176
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath constants.", @line)
|
177
|
+
elsif node.is_a? Array
|
178
|
+
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.", @line)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
return node, index
|
183
|
+
end
|
184
|
+
|
185
|
+
if node.is_a? Tree::CommentNode
|
186
|
+
while has_children
|
187
|
+
line, index = raw_next_line(index)
|
188
|
+
node << line
|
189
|
+
|
190
|
+
has_children = has_children?(index, tabs)
|
130
191
|
end
|
192
|
+
else
|
193
|
+
while has_children
|
194
|
+
child, index = build_tree(index)
|
131
195
|
|
132
|
-
|
133
|
-
|
134
|
-
|
196
|
+
if child == :constant
|
197
|
+
raise SyntaxError.new("Constants may only be declared at the root of a document.", @line)
|
198
|
+
elsif child.is_a? Array
|
199
|
+
raise SyntaxError.new("Import directives may only be used at the root of a document.", @line)
|
200
|
+
elsif child.is_a? Tree::Node
|
201
|
+
child.line = @line
|
202
|
+
node << child
|
203
|
+
end
|
204
|
+
|
205
|
+
has_children = has_children?(index, tabs)
|
206
|
+
end
|
135
207
|
end
|
136
|
-
|
208
|
+
|
137
209
|
return node, index
|
138
210
|
end
|
139
|
-
|
211
|
+
|
140
212
|
def has_children?(index, tabs)
|
141
213
|
next_line = @lines[index]
|
142
214
|
next_line && next_line[1] > tabs
|
143
215
|
end
|
144
|
-
|
216
|
+
|
217
|
+
def raw_next_line(index)
|
218
|
+
[@lines[index][0], index + 1]
|
219
|
+
end
|
220
|
+
|
145
221
|
def parse_line(line)
|
146
222
|
case line[0]
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
223
|
+
when ATTRIBUTE_CHAR
|
224
|
+
parse_attribute(line, ATTRIBUTE)
|
225
|
+
when Constant::CONSTANT_CHAR
|
226
|
+
parse_constant(line)
|
227
|
+
when COMMENT_CHAR
|
228
|
+
parse_comment(line)
|
229
|
+
when DIRECTIVE_CHAR
|
230
|
+
parse_directive(line)
|
231
|
+
else
|
232
|
+
if line =~ ATTRIBUTE_ALTERNATE_MATCHER
|
233
|
+
parse_attribute(line, ATTRIBUTE_ALTERNATE)
|
151
234
|
else
|
152
235
|
Tree::RuleNode.new(line, @options[:style])
|
236
|
+
end
|
153
237
|
end
|
154
238
|
end
|
155
|
-
|
156
|
-
def parse_attribute(line)
|
157
|
-
name, eq, value = line.scan(
|
239
|
+
|
240
|
+
def parse_attribute(line, attribute_regx)
|
241
|
+
name, eq, value = line.scan(attribute_regx)[0]
|
158
242
|
|
159
243
|
if name.nil? || value.nil?
|
160
244
|
raise SyntaxError.new("Invalid attribute: \"#{line}\"", @line)
|
161
245
|
end
|
162
|
-
|
163
|
-
if eq[0] == SCRIPT_CHAR
|
246
|
+
|
247
|
+
if eq.strip[0] == SCRIPT_CHAR
|
164
248
|
value = Sass::Constant.parse(value, @constants, @line).to_s
|
165
249
|
end
|
166
|
-
|
250
|
+
|
167
251
|
Tree::AttrNode.new(name, value, @options[:style])
|
168
252
|
end
|
169
|
-
|
253
|
+
|
170
254
|
def parse_constant(line)
|
171
255
|
name, value = line.scan(Sass::Constant::MATCH)[0]
|
172
256
|
unless name && value
|
173
257
|
raise SyntaxError.new("Invalid constant: \"#{line}\"", @line)
|
174
258
|
end
|
175
259
|
@constants[name] = Sass::Constant.parse(value, @constants, @line)
|
176
|
-
|
260
|
+
:constant
|
261
|
+
end
|
262
|
+
|
263
|
+
def parse_comment(line)
|
264
|
+
if line[1] == SASS_COMMENT_CHAR
|
265
|
+
:comment
|
266
|
+
elsif line[1] == CSS_COMMENT_CHAR
|
267
|
+
Tree::CommentNode.new(line, @options[:style])
|
268
|
+
else
|
269
|
+
Tree::RuleNode.new(line, @options[:style])
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def parse_directive(line)
|
274
|
+
directive, value = line[1..-1].split(/\s+/, 2)
|
275
|
+
|
276
|
+
case directive
|
277
|
+
when "import"
|
278
|
+
import(value)
|
279
|
+
else
|
280
|
+
raise SyntaxError.new("Unknown compiler directive: #{"@#{directive} #{value}".dump}", @line)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def import(files)
|
285
|
+
nodes = []
|
286
|
+
|
287
|
+
files.split(/,\s*/).each do |filename|
|
288
|
+
engine = nil
|
289
|
+
filename = find_file_to_import(filename)
|
290
|
+
if filename =~ /\.css$/
|
291
|
+
nodes << Tree::ValueNode.new("@import #{filename}", @options[:style])
|
292
|
+
else
|
293
|
+
File.open(filename) do |file|
|
294
|
+
new_options = @options.dup
|
295
|
+
new_options[:filename] = filename
|
296
|
+
engine = Sass::Engine.new(file.read, @options)
|
297
|
+
end
|
298
|
+
|
299
|
+
engine.constants.merge! @constants
|
300
|
+
|
301
|
+
begin
|
302
|
+
root = engine.render_to_tree
|
303
|
+
rescue Sass::SyntaxError => err
|
304
|
+
err.add_backtrace_entry(filename)
|
305
|
+
raise err
|
306
|
+
end
|
307
|
+
root.children.each do |child|
|
308
|
+
child.filename = filename
|
309
|
+
nodes << child
|
310
|
+
end
|
311
|
+
@constants = engine.constants
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
nodes
|
316
|
+
end
|
317
|
+
|
318
|
+
def find_file_to_import(filename)
|
319
|
+
was_sass = false
|
320
|
+
original_filename = filename
|
321
|
+
new_filename = nil
|
322
|
+
|
323
|
+
if filename[-5..-1] == ".sass"
|
324
|
+
filename = filename[0...-5]
|
325
|
+
was_sass = true
|
326
|
+
elsif filename[-4..-1] == ".css"
|
327
|
+
return filename
|
328
|
+
end
|
329
|
+
|
330
|
+
@options[:load_paths].each do |path|
|
331
|
+
full_path = File.join(path, filename) + '.sass'
|
332
|
+
|
333
|
+
if File.readable?(full_path)
|
334
|
+
new_filename = full_path
|
335
|
+
break
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
if new_filename.nil?
|
340
|
+
if was_sass
|
341
|
+
raise SyntaxError.new("File to import not found or unreadable: #{original_filename}", @line)
|
342
|
+
else
|
343
|
+
return filename + '.css'
|
344
|
+
end
|
345
|
+
else
|
346
|
+
new_filename
|
347
|
+
end
|
177
348
|
end
|
178
349
|
end
|
179
350
|
end
|