sass4 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +13 -0
- data/AGENTS.md +534 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +20 -0
- data/README.md +242 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/sass +13 -0
- data/bin/sass-convert +12 -0
- data/bin/scss +13 -0
- data/extra/sass-spec-ref.sh +40 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +18 -0
- data/lib/sass/cache_stores/base.rb +88 -0
- data/lib/sass/cache_stores/chain.rb +34 -0
- data/lib/sass/cache_stores/filesystem.rb +60 -0
- data/lib/sass/cache_stores/memory.rb +46 -0
- data/lib/sass/cache_stores/null.rb +25 -0
- data/lib/sass/cache_stores.rb +15 -0
- data/lib/sass/callbacks.rb +67 -0
- data/lib/sass/css.rb +407 -0
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +1236 -0
- data/lib/sass/environment.rb +236 -0
- data/lib/sass/error.rb +198 -0
- data/lib/sass/exec/base.rb +188 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +436 -0
- data/lib/sass/exec.rb +9 -0
- data/lib/sass/features.rb +48 -0
- data/lib/sass/importers/base.rb +182 -0
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +221 -0
- data/lib/sass/importers.rb +23 -0
- data/lib/sass/logger/base.rb +47 -0
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +17 -0
- data/lib/sass/media.rb +210 -0
- data/lib/sass/plugin/compiler.rb +552 -0
- data/lib/sass/plugin/configuration.rb +134 -0
- data/lib/sass/plugin/generic.rb +15 -0
- data/lib/sass/plugin/merb.rb +48 -0
- data/lib/sass/plugin/rack.rb +60 -0
- data/lib/sass/plugin/rails.rb +47 -0
- data/lib/sass/plugin/staleness_checker.rb +199 -0
- data/lib/sass/plugin.rb +134 -0
- data/lib/sass/railtie.rb +10 -0
- data/lib/sass/repl.rb +57 -0
- data/lib/sass/root.rb +7 -0
- data/lib/sass/script/css_lexer.rb +33 -0
- data/lib/sass/script/css_parser.rb +36 -0
- data/lib/sass/script/functions.rb +3103 -0
- data/lib/sass/script/lexer.rb +518 -0
- data/lib/sass/script/parser.rb +1164 -0
- data/lib/sass/script/tree/funcall.rb +314 -0
- data/lib/sass/script/tree/interpolation.rb +220 -0
- data/lib/sass/script/tree/list_literal.rb +119 -0
- data/lib/sass/script/tree/literal.rb +49 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/tree/node.rb +119 -0
- data/lib/sass/script/tree/operation.rb +149 -0
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +125 -0
- data/lib/sass/script/tree/unary_operation.rb +69 -0
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +258 -0
- data/lib/sass/script/value/bool.rb +35 -0
- data/lib/sass/script/value/callable.rb +25 -0
- data/lib/sass/script/value/color.rb +704 -0
- data/lib/sass/script/value/function.rb +19 -0
- data/lib/sass/script/value/helpers.rb +298 -0
- data/lib/sass/script/value/list.rb +135 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +44 -0
- data/lib/sass/script/value/number.rb +564 -0
- data/lib/sass/script/value/string.rb +138 -0
- data/lib/sass/script/value.rb +13 -0
- data/lib/sass/script.rb +66 -0
- data/lib/sass/scss/css_parser.rb +61 -0
- data/lib/sass/scss/parser.rb +1343 -0
- data/lib/sass/scss/rx.rb +134 -0
- data/lib/sass/scss/static_parser.rb +351 -0
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/selector/abstract_sequence.rb +112 -0
- data/lib/sass/selector/comma_sequence.rb +195 -0
- data/lib/sass/selector/pseudo.rb +291 -0
- data/lib/sass/selector/sequence.rb +661 -0
- data/lib/sass/selector/simple.rb +124 -0
- data/lib/sass/selector/simple_sequence.rb +348 -0
- data/lib/sass/selector.rb +327 -0
- data/lib/sass/shared.rb +76 -0
- data/lib/sass/source/map.rb +209 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +140 -0
- data/lib/sass/supports.rb +225 -0
- data/lib/sass/tree/at_root_node.rb +83 -0
- data/lib/sass/tree/charset_node.rb +22 -0
- data/lib/sass/tree/comment_node.rb +82 -0
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +68 -0
- data/lib/sass/tree/debug_node.rb +18 -0
- data/lib/sass/tree/directive_node.rb +59 -0
- data/lib/sass/tree/each_node.rb +24 -0
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +43 -0
- data/lib/sass/tree/for_node.rb +36 -0
- data/lib/sass/tree/function_node.rb +44 -0
- data/lib/sass/tree/if_node.rb +52 -0
- data/lib/sass/tree/import_node.rb +75 -0
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/media_node.rb +48 -0
- data/lib/sass/tree/mixin_def_node.rb +38 -0
- data/lib/sass/tree/mixin_node.rb +52 -0
- data/lib/sass/tree/node.rb +240 -0
- data/lib/sass/tree/prop_node.rb +162 -0
- data/lib/sass/tree/return_node.rb +19 -0
- data/lib/sass/tree/root_node.rb +44 -0
- data/lib/sass/tree/rule_node.rb +153 -0
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +36 -0
- data/lib/sass/tree/visitors/base.rb +72 -0
- data/lib/sass/tree/visitors/check_nesting.rb +173 -0
- data/lib/sass/tree/visitors/convert.rb +350 -0
- data/lib/sass/tree/visitors/cssize.rb +362 -0
- data/lib/sass/tree/visitors/deep_copy.rb +107 -0
- data/lib/sass/tree/visitors/extend.rb +64 -0
- data/lib/sass/tree/visitors/perform.rb +572 -0
- data/lib/sass/tree/visitors/set_options.rb +139 -0
- data/lib/sass/tree/visitors/to_css.rb +440 -0
- data/lib/sass/tree/warn_node.rb +18 -0
- data/lib/sass/tree/while_node.rb +18 -0
- data/lib/sass/util/multibyte_string_scanner.rb +151 -0
- data/lib/sass/util/normalized_map.rb +122 -0
- data/lib/sass/util/subset_map.rb +109 -0
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +1137 -0
- data/lib/sass/version.rb +120 -0
- data/lib/sass.rb +102 -0
- data/rails/init.rb +1 -0
- metadata +283 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
# A visitor for checking that all nodes are properly nested.
|
2
|
+
class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
|
3
|
+
protected
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@parents = []
|
7
|
+
@parent = nil
|
8
|
+
@current_mixin_def = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit(node)
|
12
|
+
if (error = @parent && (
|
13
|
+
try_send(@parent.class.invalid_child_method_name, @parent, node) ||
|
14
|
+
try_send(node.class.invalid_parent_method_name, @parent, node)))
|
15
|
+
raise Sass::SyntaxError.new(error)
|
16
|
+
end
|
17
|
+
super
|
18
|
+
rescue Sass::SyntaxError => e
|
19
|
+
e.modify_backtrace(:filename => node.filename, :line => node.line)
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
|
23
|
+
CONTROL_NODES = [Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
|
24
|
+
Sass::Tree::WhileNode, Sass::Tree::TraceNode]
|
25
|
+
SCRIPT_NODES = [Sass::Tree::ImportNode] + CONTROL_NODES
|
26
|
+
def visit_children(parent)
|
27
|
+
old_parent = @parent
|
28
|
+
|
29
|
+
# When checking a static tree, resolve at-roots to be sure they won't send
|
30
|
+
# nodes where they don't belong.
|
31
|
+
if parent.is_a?(Sass::Tree::AtRootNode) && parent.resolved_value
|
32
|
+
old_parents = @parents
|
33
|
+
@parents = @parents.reject {|p| parent.exclude_node?(p)}
|
34
|
+
@parent = @parents.reverse.each_with_index.
|
35
|
+
find {|p, i| !transparent_parent?(p, @parents[-i - 2])}.first
|
36
|
+
|
37
|
+
begin
|
38
|
+
return super
|
39
|
+
ensure
|
40
|
+
@parents = old_parents
|
41
|
+
@parent = old_parent
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
unless transparent_parent?(parent, old_parent)
|
46
|
+
@parent = parent
|
47
|
+
end
|
48
|
+
|
49
|
+
@parents.push parent
|
50
|
+
begin
|
51
|
+
super
|
52
|
+
ensure
|
53
|
+
@parent = old_parent
|
54
|
+
@parents.pop
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def visit_root(node)
|
59
|
+
yield
|
60
|
+
rescue Sass::SyntaxError => e
|
61
|
+
e.sass_template ||= node.template
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
|
65
|
+
def visit_import(node)
|
66
|
+
yield
|
67
|
+
rescue Sass::SyntaxError => e
|
68
|
+
e.modify_backtrace(:filename => node.children.first.filename)
|
69
|
+
e.add_backtrace(:filename => node.filename, :line => node.line)
|
70
|
+
raise e
|
71
|
+
end
|
72
|
+
|
73
|
+
def visit_mixindef(node)
|
74
|
+
@current_mixin_def, old_mixin_def = node, @current_mixin_def
|
75
|
+
yield
|
76
|
+
ensure
|
77
|
+
@current_mixin_def = old_mixin_def
|
78
|
+
end
|
79
|
+
|
80
|
+
def invalid_content_parent?(parent, child)
|
81
|
+
if @current_mixin_def
|
82
|
+
@current_mixin_def.has_content = true
|
83
|
+
nil
|
84
|
+
else
|
85
|
+
"@content may only be used within a mixin."
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def invalid_charset_parent?(parent, child)
|
90
|
+
"@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
|
91
|
+
end
|
92
|
+
|
93
|
+
VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
|
94
|
+
def invalid_extend_parent?(parent, child)
|
95
|
+
return if is_any_of?(parent, VALID_EXTEND_PARENTS)
|
96
|
+
"Extend directives may only be used within rules."
|
97
|
+
end
|
98
|
+
|
99
|
+
INVALID_IMPORT_PARENTS = CONTROL_NODES +
|
100
|
+
[Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
|
101
|
+
def invalid_import_parent?(parent, child)
|
102
|
+
unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
|
103
|
+
return "Import directives may not be used within control directives or mixins."
|
104
|
+
end
|
105
|
+
return if parent.is_a?(Sass::Tree::RootNode)
|
106
|
+
return "CSS import directives may only be used at the root of a document." if child.css_import?
|
107
|
+
rescue Sass::SyntaxError => e
|
108
|
+
e.modify_backtrace(:filename => child.imported_file.options[:filename])
|
109
|
+
e.add_backtrace(:filename => child.filename, :line => child.line)
|
110
|
+
raise e
|
111
|
+
end
|
112
|
+
|
113
|
+
def invalid_mixindef_parent?(parent, child)
|
114
|
+
return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
|
115
|
+
"Mixins may not be defined within control directives or other mixins."
|
116
|
+
end
|
117
|
+
|
118
|
+
def invalid_function_parent?(parent, child)
|
119
|
+
return if (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
|
120
|
+
"Functions may not be defined within control directives or other mixins."
|
121
|
+
end
|
122
|
+
|
123
|
+
VALID_FUNCTION_CHILDREN = [
|
124
|
+
Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
|
125
|
+
Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::ErrorNode
|
126
|
+
] + CONTROL_NODES
|
127
|
+
def invalid_function_child?(parent, child)
|
128
|
+
return if is_any_of?(child, VALID_FUNCTION_CHILDREN)
|
129
|
+
"Functions can only contain variable declarations and control directives."
|
130
|
+
end
|
131
|
+
|
132
|
+
VALID_PROP_CHILDREN = CONTROL_NODES + [Sass::Tree::CommentNode,
|
133
|
+
Sass::Tree::PropNode,
|
134
|
+
Sass::Tree::MixinNode]
|
135
|
+
def invalid_prop_child?(parent, child)
|
136
|
+
return if is_any_of?(child, VALID_PROP_CHILDREN)
|
137
|
+
"Illegal nesting: Only properties may be nested beneath properties."
|
138
|
+
end
|
139
|
+
|
140
|
+
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::KeyframeRuleNode, Sass::Tree::PropNode,
|
141
|
+
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode, Sass::Tree::MixinNode]
|
142
|
+
def invalid_prop_parent?(parent, child)
|
143
|
+
return if is_any_of?(parent, VALID_PROP_PARENTS)
|
144
|
+
"Properties are only allowed within rules, directives, mixin includes, or other properties." +
|
145
|
+
child.pseudo_class_selector_message
|
146
|
+
end
|
147
|
+
|
148
|
+
def invalid_return_parent?(parent, child)
|
149
|
+
"@return may only be used within a function." unless parent.is_a?(Sass::Tree::FunctionNode)
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Whether `parent` should be assigned to `@parent`.
|
155
|
+
def transparent_parent?(parent, grandparent)
|
156
|
+
is_any_of?(parent, SCRIPT_NODES) ||
|
157
|
+
(parent.bubbles? &&
|
158
|
+
!grandparent.is_a?(Sass::Tree::RootNode) &&
|
159
|
+
!grandparent.is_a?(Sass::Tree::AtRootNode))
|
160
|
+
end
|
161
|
+
|
162
|
+
def is_any_of?(val, classes)
|
163
|
+
classes.each do |c|
|
164
|
+
return true if val.is_a?(c)
|
165
|
+
end
|
166
|
+
false
|
167
|
+
end
|
168
|
+
|
169
|
+
def try_send(method, *args)
|
170
|
+
return unless respond_to?(method, true)
|
171
|
+
send(method, *args)
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
# A visitor for converting a Sass tree into a source string.
|
2
|
+
class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
|
3
|
+
# Runs the visitor on a tree.
|
4
|
+
#
|
5
|
+
# @param root [Tree::Node] The root node of the Sass tree.
|
6
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
7
|
+
# @param format [Symbol] `:sass` or `:scss`.
|
8
|
+
# @return [String] The Sass or SCSS source for the tree.
|
9
|
+
def self.visit(root, options, format)
|
10
|
+
new(options, format).send(:visit, root)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def initialize(options, format)
|
16
|
+
@options = options
|
17
|
+
@format = format
|
18
|
+
@tabs = 0
|
19
|
+
# 2 spaces by default
|
20
|
+
@tab_chars = @options[:indent] || " "
|
21
|
+
@is_else = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def visit_children(parent)
|
25
|
+
@tabs += 1
|
26
|
+
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
|
27
|
+
|
28
|
+
res = visit_rule_level(parent.children)
|
29
|
+
|
30
|
+
if @format == :sass
|
31
|
+
"\n" + res.rstrip + "\n"
|
32
|
+
else
|
33
|
+
" {\n" + res.rstrip + "\n#{@tab_chars * (@tabs - 1)}}\n"
|
34
|
+
end
|
35
|
+
ensure
|
36
|
+
@tabs -= 1
|
37
|
+
end
|
38
|
+
|
39
|
+
# Ensures proper spacing between top-level nodes.
|
40
|
+
def visit_root(node)
|
41
|
+
visit_rule_level(node.children)
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_charset(node)
|
45
|
+
"#{tab_str}@charset \"#{node.name}\"#{semi}\n"
|
46
|
+
end
|
47
|
+
|
48
|
+
def visit_comment(node)
|
49
|
+
value = interp_to_src(node.value)
|
50
|
+
if @format == :sass
|
51
|
+
content = value.gsub(%r{\*/$}, '').rstrip
|
52
|
+
if content =~ /\A[ \t]/
|
53
|
+
# Re-indent SCSS comments like this:
|
54
|
+
# /* foo
|
55
|
+
# bar
|
56
|
+
# baz */
|
57
|
+
content.gsub!(/^/, ' ')
|
58
|
+
content.sub!(%r{\A([ \t]*)/\*}, '/*\1')
|
59
|
+
end
|
60
|
+
|
61
|
+
if content.include?("\n")
|
62
|
+
content.gsub!(/\n \*/, "\n ")
|
63
|
+
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
|
64
|
+
sep = node.type == :silent ? "\n//" : "\n *"
|
65
|
+
if spaces >= 2
|
66
|
+
content.gsub!(/\n /, sep)
|
67
|
+
else
|
68
|
+
content.gsub!(/\n#{' ' * spaces}/, sep)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
content.gsub!(%r{\A/\*}, '//') if node.type == :silent
|
73
|
+
content.gsub!(/^/, tab_str)
|
74
|
+
content = content.rstrip + "\n"
|
75
|
+
else
|
76
|
+
spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
|
77
|
+
content = if node.type == :silent
|
78
|
+
value.gsub(%r{^[/ ]\*}, '//').gsub(%r{ *\*/$}, '')
|
79
|
+
else
|
80
|
+
value
|
81
|
+
end.gsub(/^/, spaces) + "\n"
|
82
|
+
end
|
83
|
+
content
|
84
|
+
end
|
85
|
+
|
86
|
+
def visit_debug(node)
|
87
|
+
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
def visit_error(node)
|
91
|
+
"#{tab_str}@error #{node.expr.to_sass(@options)}#{semi}\n"
|
92
|
+
end
|
93
|
+
|
94
|
+
def visit_directive(node)
|
95
|
+
res = "#{tab_str}#{interp_to_src(node.value)}"
|
96
|
+
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2')
|
97
|
+
return res + "#{semi}\n" unless node.has_children
|
98
|
+
res + yield
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_each(node)
|
102
|
+
vars = node.vars.map {|var| "$#{dasherize(var)}"}.join(", ")
|
103
|
+
"#{tab_str}@each #{vars} in #{node.list.to_sass(@options)}#{yield}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def visit_extend(node)
|
107
|
+
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}" +
|
108
|
+
"#{' !optional' if node.optional?}#{semi}\n"
|
109
|
+
end
|
110
|
+
|
111
|
+
def visit_for(node)
|
112
|
+
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
|
113
|
+
"#{node.exclusive ? 'to' : 'through'} #{node.to.to_sass(@options)}#{yield}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def visit_function(node)
|
117
|
+
args = node.args.map do |v, d|
|
118
|
+
d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
|
119
|
+
end.join(", ")
|
120
|
+
if node.splat
|
121
|
+
args << ", " unless node.args.empty?
|
122
|
+
args << node.splat.to_sass(@options) << "..."
|
123
|
+
end
|
124
|
+
|
125
|
+
"#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def visit_if(node)
|
129
|
+
name =
|
130
|
+
if !@is_else
|
131
|
+
"if"
|
132
|
+
elsif node.expr
|
133
|
+
"else if"
|
134
|
+
else
|
135
|
+
"else"
|
136
|
+
end
|
137
|
+
@is_else = false
|
138
|
+
str = "#{tab_str}@#{name}"
|
139
|
+
str << " #{node.expr.to_sass(@options)}" if node.expr
|
140
|
+
str << yield
|
141
|
+
@is_else = true
|
142
|
+
str << visit(node.else) if node.else
|
143
|
+
str
|
144
|
+
ensure
|
145
|
+
@is_else = false
|
146
|
+
end
|
147
|
+
|
148
|
+
def visit_import(node)
|
149
|
+
quote = @format == :scss ? '"' : ''
|
150
|
+
"#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
|
151
|
+
end
|
152
|
+
|
153
|
+
def visit_media(node)
|
154
|
+
"#{tab_str}@media #{query_interp_to_src(node.query)}#{yield}"
|
155
|
+
end
|
156
|
+
|
157
|
+
def visit_supports(node)
|
158
|
+
"#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def visit_cssimport(node)
|
162
|
+
if node.uri.is_a?(Sass::Script::Tree::Node)
|
163
|
+
str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
|
164
|
+
else
|
165
|
+
str = "#{tab_str}@import #{node.uri}"
|
166
|
+
end
|
167
|
+
str << " supports(#{node.supports_condition.to_src(@options)})" if node.supports_condition
|
168
|
+
str << " #{interp_to_src(node.query)}" unless node.query.empty?
|
169
|
+
"#{str}#{semi}\n"
|
170
|
+
end
|
171
|
+
|
172
|
+
def visit_mixindef(node)
|
173
|
+
args =
|
174
|
+
if node.args.empty? && node.splat.nil?
|
175
|
+
""
|
176
|
+
else
|
177
|
+
str = '('
|
178
|
+
str << node.args.map do |v, d|
|
179
|
+
if d
|
180
|
+
"#{v.to_sass(@options)}: #{d.to_sass(@options)}"
|
181
|
+
else
|
182
|
+
v.to_sass(@options)
|
183
|
+
end
|
184
|
+
end.join(", ")
|
185
|
+
|
186
|
+
if node.splat
|
187
|
+
str << ", " unless node.args.empty?
|
188
|
+
str << node.splat.to_sass(@options) << '...'
|
189
|
+
end
|
190
|
+
|
191
|
+
str << ')'
|
192
|
+
end
|
193
|
+
|
194
|
+
"#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
|
195
|
+
end
|
196
|
+
|
197
|
+
def visit_mixin(node)
|
198
|
+
arg_to_sass = lambda do |arg|
|
199
|
+
sass = arg.to_sass(@options)
|
200
|
+
sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
|
201
|
+
sass
|
202
|
+
end
|
203
|
+
|
204
|
+
unless node.args.empty? && node.keywords.empty? && node.splat.nil?
|
205
|
+
args = node.args.map(&arg_to_sass)
|
206
|
+
keywords = node.keywords.as_stored.to_a.map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}
|
207
|
+
|
208
|
+
if node.splat
|
209
|
+
splat = "#{arg_to_sass[node.splat]}..."
|
210
|
+
kwarg_splat = "#{arg_to_sass[node.kwarg_splat]}..." if node.kwarg_splat
|
211
|
+
end
|
212
|
+
|
213
|
+
arglist = "(#{[args, splat, keywords, kwarg_splat].flatten.compact.join(', ')})"
|
214
|
+
end
|
215
|
+
"#{tab_str}#{@format == :sass ? '+' : '@include '}" +
|
216
|
+
"#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
|
217
|
+
end
|
218
|
+
|
219
|
+
def visit_content(node)
|
220
|
+
"#{tab_str}@content#{semi}\n"
|
221
|
+
end
|
222
|
+
|
223
|
+
def visit_prop(node)
|
224
|
+
res = tab_str + node.declaration(@options, @format)
|
225
|
+
return res + semi + "\n" if node.children.empty?
|
226
|
+
res + yield.rstrip + semi + "\n"
|
227
|
+
end
|
228
|
+
|
229
|
+
def visit_return(node)
|
230
|
+
"#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
|
231
|
+
end
|
232
|
+
|
233
|
+
def visit_rule(node)
|
234
|
+
rule = node.parsed_rules ? [node.parsed_rules.to_s] : node.rule
|
235
|
+
if @format == :sass
|
236
|
+
name = selector_to_sass(rule)
|
237
|
+
name = "\\" + name if name[0] == ?:
|
238
|
+
name.gsub(/^/, tab_str) + yield
|
239
|
+
elsif @format == :scss
|
240
|
+
name = selector_to_scss(rule)
|
241
|
+
res = name + yield
|
242
|
+
if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
|
243
|
+
res.slice!(-3..-1)
|
244
|
+
res << "\n" << tab_str << "}\n"
|
245
|
+
end
|
246
|
+
res
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def visit_variable(node)
|
251
|
+
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}" +
|
252
|
+
"#{' !global' if node.global}#{' !default' if node.guarded}#{semi}\n"
|
253
|
+
end
|
254
|
+
|
255
|
+
def visit_warn(node)
|
256
|
+
"#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
|
257
|
+
end
|
258
|
+
|
259
|
+
def visit_while(node)
|
260
|
+
"#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
|
261
|
+
end
|
262
|
+
|
263
|
+
def visit_atroot(node)
|
264
|
+
if node.query
|
265
|
+
"#{tab_str}@at-root #{query_interp_to_src(node.query)}#{yield}"
|
266
|
+
elsif node.children.length == 1 && node.children.first.is_a?(Sass::Tree::RuleNode)
|
267
|
+
rule = node.children.first
|
268
|
+
"#{tab_str}@at-root #{selector_to_src(rule.rule).lstrip}#{visit_children(rule)}"
|
269
|
+
else
|
270
|
+
"#{tab_str}@at-root#{yield}"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def visit_keyframerule(node)
|
275
|
+
"#{tab_str}#{node.resolved_value}#{yield}"
|
276
|
+
end
|
277
|
+
|
278
|
+
private
|
279
|
+
|
280
|
+
# Visit rule-level nodes and return their conversion with appropriate
|
281
|
+
# whitespace added.
|
282
|
+
def visit_rule_level(nodes)
|
283
|
+
(nodes + [nil]).each_cons(2).map do |child, nxt|
|
284
|
+
visit(child) +
|
285
|
+
if nxt &&
|
286
|
+
(child.is_a?(Sass::Tree::CommentNode) && child.line + child.lines + 1 == nxt.line) ||
|
287
|
+
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
|
288
|
+
child.line + 1 == nxt.line) ||
|
289
|
+
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
|
290
|
+
child.line + 1 == nxt.line) ||
|
291
|
+
(child.is_a?(Sass::Tree::PropNode) && nxt.is_a?(Sass::Tree::PropNode)) ||
|
292
|
+
(child.is_a?(Sass::Tree::MixinNode) && nxt.is_a?(Sass::Tree::MixinNode) &&
|
293
|
+
child.line + 1 == nxt.line)
|
294
|
+
""
|
295
|
+
else
|
296
|
+
"\n"
|
297
|
+
end
|
298
|
+
end.join.rstrip + "\n"
|
299
|
+
end
|
300
|
+
|
301
|
+
def interp_to_src(interp)
|
302
|
+
interp.map {|r| r.is_a?(String) ? r : r.to_sass(@options)}.join
|
303
|
+
end
|
304
|
+
|
305
|
+
# Like interp_to_src, but removes the unnecessary `#{}` around the keys and
|
306
|
+
# values in query expressions.
|
307
|
+
def query_interp_to_src(interp)
|
308
|
+
interp = interp.map do |e|
|
309
|
+
next e unless e.is_a?(Sass::Script::Tree::Literal)
|
310
|
+
next e unless e.value.is_a?(Sass::Script::Value::String)
|
311
|
+
e.value.value
|
312
|
+
end
|
313
|
+
|
314
|
+
interp_to_src(interp)
|
315
|
+
end
|
316
|
+
|
317
|
+
def selector_to_src(sel)
|
318
|
+
@format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
|
319
|
+
end
|
320
|
+
|
321
|
+
def selector_to_sass(sel)
|
322
|
+
sel.map do |r|
|
323
|
+
if r.is_a?(String)
|
324
|
+
r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
|
325
|
+
else
|
326
|
+
r.to_sass(@options)
|
327
|
+
end
|
328
|
+
end.join
|
329
|
+
end
|
330
|
+
|
331
|
+
def selector_to_scss(sel)
|
332
|
+
interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
|
333
|
+
end
|
334
|
+
|
335
|
+
def semi
|
336
|
+
@format == :sass ? "" : ";"
|
337
|
+
end
|
338
|
+
|
339
|
+
def tab_str
|
340
|
+
@tab_chars * @tabs
|
341
|
+
end
|
342
|
+
|
343
|
+
def dasherize(s)
|
344
|
+
if @options[:dasherize]
|
345
|
+
s.tr('_', '-')
|
346
|
+
else
|
347
|
+
s
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|