less_to_sass 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +70 -0
- data/bin/less2sass +12 -0
- data/bin/sass2less +12 -0
- data/lib/less2sass/constants.rb +5 -0
- data/lib/less2sass/error.rb +100 -0
- data/lib/less2sass/exec/base.rb +83 -0
- data/lib/less2sass/exec/conversion.rb +102 -0
- data/lib/less2sass/exec.rb +2 -0
- data/lib/less2sass/js/less_parser.js +48 -0
- data/lib/less2sass/less/ast_handler.rb +33 -0
- data/lib/less2sass/less/environment.rb +212 -0
- data/lib/less2sass/less/parser.rb +131 -0
- data/lib/less2sass/less/tree/alpha_node.rb +9 -0
- data/lib/less2sass/less/tree/anonymous_node.rb +43 -0
- data/lib/less2sass/less/tree/assignment_node.rb +10 -0
- data/lib/less2sass/less/tree/attribute_node.rb +11 -0
- data/lib/less2sass/less/tree/call_node.rb +65 -0
- data/lib/less2sass/less/tree/color_node.rb +27 -0
- data/lib/less2sass/less/tree/combinator_node.rb +18 -0
- data/lib/less2sass/less/tree/comment_node.rb +58 -0
- data/lib/less2sass/less/tree/condition_node.rb +13 -0
- data/lib/less2sass/less/tree/detached_ruleset_node.rb +10 -0
- data/lib/less2sass/less/tree/dimension_node.rb +26 -0
- data/lib/less2sass/less/tree/directive_node.rb +40 -0
- data/lib/less2sass/less/tree/element_node.rb +32 -0
- data/lib/less2sass/less/tree/expression_node.rb +73 -0
- data/lib/less2sass/less/tree/extend_node.rb +14 -0
- data/lib/less2sass/less/tree/import_node.rb +14 -0
- data/lib/less2sass/less/tree/keyword_node.rb +49 -0
- data/lib/less2sass/less/tree/media_node.rb +12 -0
- data/lib/less2sass/less/tree/mixin_call_node.rb +13 -0
- data/lib/less2sass/less/tree/mixin_definition_node.rb +24 -0
- data/lib/less2sass/less/tree/negative_node.rb +9 -0
- data/lib/less2sass/less/tree/node.rb +212 -0
- data/lib/less2sass/less/tree/operation_node.rb +63 -0
- data/lib/less2sass/less/tree/paren_node.rb +9 -0
- data/lib/less2sass/less/tree/quoted_node.rb +64 -0
- data/lib/less2sass/less/tree/rule_node.rb +119 -0
- data/lib/less2sass/less/tree/ruleset_call_node.rb +9 -0
- data/lib/less2sass/less/tree/ruleset_node.rb +82 -0
- data/lib/less2sass/less/tree/selector_node.rb +27 -0
- data/lib/less2sass/less/tree/unicode_descriptor_node.rb +9 -0
- data/lib/less2sass/less/tree/unit_node.rb +17 -0
- data/lib/less2sass/less/tree/url_node.rb +22 -0
- data/lib/less2sass/less/tree/value_node.rb +53 -0
- data/lib/less2sass/less/tree/variable_node.rb +43 -0
- data/lib/less2sass/less/tree.rb +34 -0
- data/lib/less2sass/less.rb +2 -0
- data/lib/less2sass/sass/ast_handler.rb +57 -0
- data/lib/less2sass/sass/parser.rb +20 -0
- data/lib/less2sass/sass.rb +2 -0
- data/lib/less2sass/util.rb +36 -0
- data/lib/less2sass.rb +9 -0
- metadata +163 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
# The lexical environment for Less.
|
4
|
+
# Keeps track of variable and mixin definitions.
|
5
|
+
# The environment differentiates between simple
|
6
|
+
# and constructed(dynamic) variable definitions.
|
7
|
+
#
|
8
|
+
# A new environment is created for each level of Less nesting.
|
9
|
+
# This allows variables to be lexically scoped.
|
10
|
+
# Each environment refers to the environment in the upper scope,
|
11
|
+
# unless it is the global environment, thus having access to
|
12
|
+
# variables defined in enclosing scopes. New variables are defined
|
13
|
+
# locally.
|
14
|
+
class Environment
|
15
|
+
# The enclosing environment, or nil if it's the
|
16
|
+
# global environment.
|
17
|
+
#
|
18
|
+
# @return [Less2Sass::Less::Environment]
|
19
|
+
attr_reader :parent
|
20
|
+
|
21
|
+
# Ordered list of static variable definitions.
|
22
|
+
# Examples:
|
23
|
+
# - @var: 50px;
|
24
|
+
#
|
25
|
+
# @return [Array<Less2Sass::Less::Tree::RuleNode>]
|
26
|
+
attr_reader :static_var_def_rules
|
27
|
+
|
28
|
+
# Ordered list of constructed variable definitions.
|
29
|
+
# Examples:
|
30
|
+
# - @var: @var2;
|
31
|
+
# - @var1: @var2 + 10px;
|
32
|
+
#
|
33
|
+
# @return [Array<Less2Sass::Less::Tree::RuleNode>]
|
34
|
+
attr_reader :dynamic_var_def_rules
|
35
|
+
|
36
|
+
# A list of mixin calls.
|
37
|
+
#
|
38
|
+
# @return [Array<Less2Sass::Less::Tree::RuleNode>]
|
39
|
+
attr_reader :mixin_call_rules
|
40
|
+
|
41
|
+
# @param parent (see :parent)
|
42
|
+
def initialize(parent = nil)
|
43
|
+
@parent = parent
|
44
|
+
@variables_to_reorder = {}
|
45
|
+
@static_var_def_rules = {}
|
46
|
+
@dynamic_var_def_rules = []
|
47
|
+
@rules = []
|
48
|
+
@rest = []
|
49
|
+
@mixin_call_rules = []
|
50
|
+
end
|
51
|
+
|
52
|
+
# Places an array of nodes into this environment.
|
53
|
+
#
|
54
|
+
# @param [Array<Less2Sass::Less::Tree::Node>] objs
|
55
|
+
# child nodes of a context creator node
|
56
|
+
# @see #put
|
57
|
+
def set_environment(objs)
|
58
|
+
objs.each { |obj| set(obj) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Builds the `dynamic_var_def_rules` ordered array.
|
62
|
+
#
|
63
|
+
# Processes lazy loaded variables by importing their definition
|
64
|
+
# if they are overridden of this environment's scope. Furthermore,
|
65
|
+
# it reorders the variable definitions.
|
66
|
+
# The only rule is, that the referenced variables should be
|
67
|
+
# placed before their first referral. If no variable definition
|
68
|
+
# references the the current iteration's variable, it shall be
|
69
|
+
# put at the end of the array, since it could reference any of the
|
70
|
+
# previously defined variables.
|
71
|
+
#
|
72
|
+
# @note does not detect recursive variable definitions
|
73
|
+
# @return [void]
|
74
|
+
# @todo implement detection in future releases
|
75
|
+
def build
|
76
|
+
process_lazy_loading
|
77
|
+
to_reorder = @variables_to_reorder.values
|
78
|
+
unless to_reorder.empty?
|
79
|
+
@dynamic_var_def_rules << to_reorder.shift
|
80
|
+
to_reorder.each do |var|
|
81
|
+
@dynamic_var_def_rules.each_with_index do |o_var, index|
|
82
|
+
if o_var.references?(var.name)
|
83
|
+
@dynamic_var_def_rules.insert(index, var)
|
84
|
+
break
|
85
|
+
end
|
86
|
+
@dynamic_var_def_rules.insert(index + 1, var) if @dynamic_var_def_rules.last.eql?(o_var)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Reorders the child nodes of another node, that creates an
|
93
|
+
# environment, resp. an execution context.
|
94
|
+
#
|
95
|
+
# The order should be:
|
96
|
+
#
|
97
|
+
# 1. Simple/static variables
|
98
|
+
# These do not reference any other variable(s) in their definition.
|
99
|
+
#
|
100
|
+
# 2. Complex/dynamic variables
|
101
|
+
# These can reference either the static variables in the same context,
|
102
|
+
# other variables from the outer environment, or each other without any
|
103
|
+
# cross-reference, also known as recursive reference.
|
104
|
+
#
|
105
|
+
# TODO: maybe directives should be also sorted out and put before the mixin calls
|
106
|
+
# 3. Other nodes (directives, rules, rule sets, etc.)
|
107
|
+
#
|
108
|
+
# 4. Rules (selector + declarations)
|
109
|
+
# The Sass rules (Less rule sets) create a new environment and could redefine
|
110
|
+
# the variables' used after their declaration.
|
111
|
+
#
|
112
|
+
# 5. Mixin calls
|
113
|
+
# These mixins can reference the rule sets created one step above.
|
114
|
+
#
|
115
|
+
# @return [Array<Less2Sass::Less::Tree::Node>] the reordered list of nodes
|
116
|
+
def get_ordered_child_nodes
|
117
|
+
@static_var_def_rules.values + @dynamic_var_def_rules + @rest + @rules + @mixin_call_rules
|
118
|
+
end
|
119
|
+
|
120
|
+
def variable_defined?(name)
|
121
|
+
@static_var_def_rules[name] || @variables_to_reorder[name]
|
122
|
+
end
|
123
|
+
|
124
|
+
def find_variable_definition_if_dynamic(name)
|
125
|
+
variable = @variables_to_reorder[name]
|
126
|
+
return variable if variable
|
127
|
+
@parent ? @parent.find_variable_definition_if_dynamic(name) : nil
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# Puts the nodes of this environment's scope into
|
133
|
+
# the appropriate container.
|
134
|
+
#
|
135
|
+
# Sorts out the variable definitions and mixin calls
|
136
|
+
# from the rest. The variable definitions are saved
|
137
|
+
# into hashes under their names as keys. If a variable
|
138
|
+
# is defined multiple times, only the last definition
|
139
|
+
# will be kept.
|
140
|
+
#
|
141
|
+
# @param [Less2Sass::Less::Tree::Node] obj
|
142
|
+
# the rule to be placed into the environment
|
143
|
+
# @return [void]
|
144
|
+
def set(obj)
|
145
|
+
if obj.is_a?(Less2Sass::Less::Tree::RulesetNode)
|
146
|
+
@rules << obj
|
147
|
+
elsif obj.is_a?(Less2Sass::Less::Tree::RuleNode)
|
148
|
+
if obj.is_variable_definition?
|
149
|
+
referenced_vars = obj.get_referenced_variable_names
|
150
|
+
if referenced_vars.empty?
|
151
|
+
@static_var_def_rules[obj.name] = obj
|
152
|
+
else
|
153
|
+
obj.ref_vars = referenced_vars
|
154
|
+
@variables_to_reorder[obj.name] = obj
|
155
|
+
end
|
156
|
+
else
|
157
|
+
@rest << obj
|
158
|
+
end
|
159
|
+
elsif obj.is_a?(Less2Sass::Less::Tree::MixinCallNode)
|
160
|
+
@mixin_call_rules << obj
|
161
|
+
elsif obj.is_a?(Less2Sass::Less::Tree::SelectorNode)
|
162
|
+
return # ignore selectors
|
163
|
+
else
|
164
|
+
@rest << obj
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Processes lazy loaded variables by importing their definition
|
169
|
+
# if they are overridden of this environment's scope.
|
170
|
+
#
|
171
|
+
# Imports all those variable definitions, whose
|
172
|
+
# members are overridden by the variable definitions
|
173
|
+
# of the current scope.
|
174
|
+
#
|
175
|
+
# @return [Void]
|
176
|
+
def process_lazy_loading
|
177
|
+
rules_to_check = @dynamic_var_def_rules + @rules + @rest + @mixin_call_rules
|
178
|
+
rules_to_check.each do |rule|
|
179
|
+
rules_to_check += [rule.selectors].flatten if rule.is_a?(Less2Sass::Less::Tree::RulesetNode)
|
180
|
+
rule.ref_vars.each do |variable|
|
181
|
+
import_definitions_of(variable)
|
182
|
+
end unless rule.creates_context?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Imports the appropriate variable definitions
|
187
|
+
# into the current scope.
|
188
|
+
#
|
189
|
+
# Those definitions will be imported, that contain a
|
190
|
+
# reference to a variable, that is redefined in
|
191
|
+
# the current scope. If such a variable is not redefined,
|
192
|
+
# its definition is looked for such a variable.
|
193
|
+
# The process continues recursively just like the process
|
194
|
+
# of lazy loading.
|
195
|
+
#
|
196
|
+
# @param [String] name the variable's name
|
197
|
+
# @return [Void]
|
198
|
+
def import_definitions_of(name)
|
199
|
+
if @parent
|
200
|
+
definition = @parent.find_variable_definition_if_dynamic(name)
|
201
|
+
definition.ref_vars.each do |var|
|
202
|
+
if variable_defined?(var)
|
203
|
+
@variables_to_reorder[definition.name] = definition unless @variables_to_reorder[definition.name]
|
204
|
+
else
|
205
|
+
import_definitions_of(var)
|
206
|
+
end
|
207
|
+
end if definition
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'less2sass/less/tree'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Less2Sass
|
5
|
+
module Less
|
6
|
+
# The parser for Less.
|
7
|
+
# It parses a Less project into a tree of {Less2Sass::Less::Tree::Node}s.
|
8
|
+
# The project is meant as one or more files.
|
9
|
+
class Parser
|
10
|
+
# @param [File, IO] input The source project to parse.
|
11
|
+
def initialize(input)
|
12
|
+
@input = input
|
13
|
+
@options = input == $stdin ? "-stdin \"#{Util.read_stdin_as_multiline_arg}\"" : File.expand_path(input)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Checks, whether the Less project can be compiled.
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
# @raise [Less2Sass::LessCompilationError] if the project can't be compiled.
|
20
|
+
def check_syntax
|
21
|
+
response = `lessc --lint #{File.expand_path(@input)} 2>&1 >/dev/null`
|
22
|
+
raise LessCompilationError, response unless response.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Parses a Less project.
|
26
|
+
#
|
27
|
+
# @return [Less2Sass::Less::Tree::Node] The root node of the project tree
|
28
|
+
# @raise [Less2Sass::LessSyntaxError] if there's a syntax error in the document
|
29
|
+
# @raise [Less2Sass::LessImportNotFoundError] if any of the imports can't be found
|
30
|
+
def parse
|
31
|
+
string_ast = `node #{Util.scope(PARSER)} #{@options}`
|
32
|
+
json_ast = JSON.parse(string_ast)
|
33
|
+
if json_ast['class'] == 'error'
|
34
|
+
raise LessImportNotFoundError, json_ast if json_ast['type'] == 'File'
|
35
|
+
raise LessSyntaxError, json_ast if json_ast['type'] == 'Parse'
|
36
|
+
end
|
37
|
+
build_tree(json_ast)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Converts the parsed Less project from JSON object
|
41
|
+
# to a {Less2Sass::Less::Tree::Node} representation.
|
42
|
+
#
|
43
|
+
# Note, that this method recursively calls itself.
|
44
|
+
#
|
45
|
+
# @param [Hash] json the JSON object representing the Less AST
|
46
|
+
# @param [Less2Sass::Less::Tree::Node] parent the respective parent node
|
47
|
+
# of the currently processed node.
|
48
|
+
# @return [Less2Sass::Less::Tree::Node] The root node of the project tree
|
49
|
+
# @raise [Less2Sass::LessASTParserError] if something unexpected happens.
|
50
|
+
def build_tree(json, parent = nil)
|
51
|
+
node = create_node(json['class'], parent)
|
52
|
+
|
53
|
+
json.each do |k, v|
|
54
|
+
key = instance_var_sym(k)
|
55
|
+
|
56
|
+
if v.is_a?(Hash)
|
57
|
+
# Is the hash a node, or a simple value?
|
58
|
+
if node?(v)
|
59
|
+
# The node should be referenced under the original key, as well as
|
60
|
+
# in the child nodes, so it's easier to handle conversion
|
61
|
+
# and still be able to traverse the full tree. Since these would be
|
62
|
+
# the same objects, the changes made during the transformation
|
63
|
+
# process would be reflected at both places.
|
64
|
+
subnode = build_tree(v, node)
|
65
|
+
node.instance_variable_set(key, subnode)
|
66
|
+
node << subnode
|
67
|
+
else
|
68
|
+
node.instance_variable_set(key, v)
|
69
|
+
end
|
70
|
+
elsif v.is_a?(Array)
|
71
|
+
if node?(v[0])
|
72
|
+
# It's an array of nodes
|
73
|
+
subnodes = []
|
74
|
+
v.each do |item|
|
75
|
+
# Something is very wrong if the item is not another node
|
76
|
+
raise LessASTParserError, item.class.to_s unless node?(item)
|
77
|
+
subnode = build_tree(item, node)
|
78
|
+
subnodes << subnode
|
79
|
+
node << subnode
|
80
|
+
end
|
81
|
+
value = subnodes.length == 1 ? subnodes[0] : subnodes
|
82
|
+
node.instance_variable_set(key, value)
|
83
|
+
else
|
84
|
+
# It's a simple array containing other values than nodes
|
85
|
+
node.instance_variable_set(key, v)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
# Simple key-value pair
|
89
|
+
next if k == 'class'
|
90
|
+
node.instance_variable_set(key, v)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
node
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
PARSER = 'lib/less2sass/js/less_parser.js'.freeze
|
100
|
+
|
101
|
+
# Creates the node in a Ruby object-like representation.
|
102
|
+
#
|
103
|
+
# @param [String] class_name the classname of the node to be created
|
104
|
+
# @param [Less2Sass::Less::Tree::Node] parent the parent node of the one
|
105
|
+
# currently being created
|
106
|
+
# @return [Less2Sass::Less::Tree::Node] an AST node
|
107
|
+
def create_node(class_name, parent)
|
108
|
+
Tree.const_get(class_name.to_s + 'Node')
|
109
|
+
.new(parent)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Creates a symbol as reference to a member object,
|
113
|
+
# so it can be set dynamically.
|
114
|
+
#
|
115
|
+
# @param [String] key the string representation
|
116
|
+
# of an object's member
|
117
|
+
# @return [Symbol] the symbolic reference to an object's member
|
118
|
+
def instance_var_sym(key)
|
119
|
+
"@#{key}".to_sym
|
120
|
+
end
|
121
|
+
|
122
|
+
def node?(json)
|
123
|
+
json.is_a?(Hash) && json.key?('class')
|
124
|
+
end
|
125
|
+
|
126
|
+
def hash_value?(hash)
|
127
|
+
!hash.key?('class')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Represents a CSS literal in Less.
|
5
|
+
#
|
6
|
+
# Its Sass equivalent is {::Sass::Script::Value::Base}
|
7
|
+
# wrapped in {::Sass::Script::Tree::Literal}.
|
8
|
+
class AnonymousNode < Node
|
9
|
+
attr_accessor :value
|
10
|
+
attr_accessor :index
|
11
|
+
attr_accessor :mapLines
|
12
|
+
attr_accessor :currentFileInfo
|
13
|
+
attr_accessor :rulesetLike
|
14
|
+
|
15
|
+
# Converts to a {::Sass::Script::Tree::Literal} that
|
16
|
+
# encapsulates a {::Sass::Script::Value::Base}.
|
17
|
+
#
|
18
|
+
# @note Never seen a different equivalent than String
|
19
|
+
# or a Number but can't be sure.
|
20
|
+
# @raise FeatureConversionError if `@value` not a String
|
21
|
+
# or a Numeric.
|
22
|
+
# @return [::Sass::Script::Tree::Literal]
|
23
|
+
# @see Node#to_sass
|
24
|
+
def to_sass
|
25
|
+
subnode ||=
|
26
|
+
begin
|
27
|
+
if @value.is_a?(String)
|
28
|
+
# Always an :identifier - the default value of the 2nd param
|
29
|
+
::Sass::Script::Value::String.new(@value)
|
30
|
+
elsif @value.is_a?(Numeric)
|
31
|
+
# Anonymous nodes do not have a numerator nor a denominator
|
32
|
+
::Sass::Script::Value::Number.new(@value)
|
33
|
+
else
|
34
|
+
raise FeatureConversionError, self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return subnode if subnode.is_a?(::Sass::Script::Tree::Literal)
|
38
|
+
node(::Sass::Script::Tree::Literal.new(subnode), line)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Represents a function call node. It can be either
|
5
|
+
# a CSS or a Less built-in function.
|
6
|
+
#
|
7
|
+
# Its Sass equivalent is {::Sass::Script::Tree::Funcall}.
|
8
|
+
class CallNode < Node
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :args
|
11
|
+
attr_accessor :index
|
12
|
+
attr_accessor :currentFileInfo
|
13
|
+
|
14
|
+
# TODO: Map Less built-in functions to Sass built-in functions, null if there is no equivalent
|
15
|
+
|
16
|
+
MISC_FUNCTIONS = %w(color image-size image-width image-height
|
17
|
+
convert data-uri default unit get-unit svg-gradient).freeze
|
18
|
+
|
19
|
+
STRING_FUNCTIONS = %w(escape e % replace).freeze
|
20
|
+
|
21
|
+
LIST_FUNCTIONS = %w(length extract).freeze
|
22
|
+
|
23
|
+
MATH_FUNCTIONS = %w(ceil floor percentage round sqrt abs sin
|
24
|
+
asin cos acos tan atan pi pow mod min max).freeze
|
25
|
+
|
26
|
+
TYPE_FUNCTIONS = %w(isnumber isstring iscolor iskeyword isurl
|
27
|
+
ispixel isem ispercentage isunit isruleset).freeze
|
28
|
+
|
29
|
+
COLOR_DEFINITION_FUNCTIONS = %w(rgb rgba argb hsl hsla hsv hsva).freeze
|
30
|
+
|
31
|
+
COLOR_CHANNEL_FUNCTIONS = %w(hue saturation lightness hsvhue hsvsaturation
|
32
|
+
hsvvalue red green blue alpha luma luminance).freeze
|
33
|
+
|
34
|
+
COLOR_OPERATION_FUNCTIONS = %w(saturate desaturate lighten darken fadein
|
35
|
+
fadeout fade spin mix tint shade greyscale contrast).freeze
|
36
|
+
|
37
|
+
COLOR_BLENDING_FUNCTIONS = %w(multiply screen overlay softlight hardlight
|
38
|
+
difference exclusion average negation).freeze
|
39
|
+
|
40
|
+
# @todo built-in functions should be converted by the mapping
|
41
|
+
# if there is no equivalent, a custom conversion should be implemented.
|
42
|
+
# If the {Hash} does not contain the function, most probably, it's a
|
43
|
+
# CSS built-in function and it should be copied as it is.
|
44
|
+
#
|
45
|
+
# @note In its current version it could output unrecognizable functions
|
46
|
+
# by the Sass compiler and/or interpreter.
|
47
|
+
# @return [::Sass::Script::Tree::Funcall]
|
48
|
+
# @see Node#to_sass
|
49
|
+
def to_sass
|
50
|
+
node(::Sass::Script::Tree::Funcall.new(@name, get_args, ::Sass::Util::NormalizedMap.new, nil, nil), line)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def get_args
|
56
|
+
if @args.is_a?(Array)
|
57
|
+
@args.inject([]) { |args, arg| args << arg.to_sass }.flatten
|
58
|
+
else
|
59
|
+
[@args.to_sass]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# CSS color representation.
|
5
|
+
#
|
6
|
+
# Usually appears at variable definitions.
|
7
|
+
# Example:
|
8
|
+
# - `@color: #fff;` rule contains ColorNode
|
9
|
+
# - `color: #fff;` property declaration does
|
10
|
+
# not contain ColorNode
|
11
|
+
#
|
12
|
+
# The Sass equivalent is {::Sass::Script::Value::Color}.
|
13
|
+
class ColorNode < Node
|
14
|
+
attr_accessor :rgb
|
15
|
+
attr_accessor :alpha
|
16
|
+
attr_accessor :value
|
17
|
+
|
18
|
+
# @return [::Sass::Script::Value::Color]
|
19
|
+
# @see Node#to_sass
|
20
|
+
def to_sass
|
21
|
+
color = node(::Sass::Script::Value::Color.new(@rgb, @value), nil)
|
22
|
+
node(::Sass::Script::Tree::Literal.new(color), line)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Represents the separator between some nodes.
|
5
|
+
# No equivalent in Sass.
|
6
|
+
class CombinatorNode < Node
|
7
|
+
# @return [String]
|
8
|
+
attr_accessor :value
|
9
|
+
# @return [Boolean]
|
10
|
+
attr_accessor :emptyOrWhitespace
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Represents either a single-line or a block comment
|
5
|
+
# Unfortunately, there is no way to find out, whether
|
6
|
+
# the comment has been put on a new line or it just
|
7
|
+
# continues on the same line as a rule, for instance.
|
8
|
+
#
|
9
|
+
# Examples:
|
10
|
+
# color: #fff; //line comment
|
11
|
+
# or
|
12
|
+
# color: #fff;
|
13
|
+
# //line comment
|
14
|
+
#
|
15
|
+
# The AST will be absolutely identical in both cases.
|
16
|
+
# So let's come up with a convention:
|
17
|
+
# - line comments will be put on the same line as
|
18
|
+
# the previous node
|
19
|
+
# - block comments will be put on new lines
|
20
|
+
class CommentNode < Node
|
21
|
+
attr_accessor :value
|
22
|
+
attr_accessor :isLineComment
|
23
|
+
attr_accessor :currentFileInfo
|
24
|
+
|
25
|
+
def to_sass
|
26
|
+
line = line(@isLineComment ? :current : :new)
|
27
|
+
node(::Sass::Tree::CommentNode.new([@value], type), line)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Specifies the type of the comment.
|
33
|
+
#
|
34
|
+
# There are 3 types of comments in Sass:
|
35
|
+
# - :normal (output to CSS except in :compressed mode)
|
36
|
+
# - :silent (never output to CSS)
|
37
|
+
# - :loud (output to CSS even in :compressed mode)
|
38
|
+
#
|
39
|
+
# To evaluate the type correctly, the original implementation
|
40
|
+
# needs to be used.
|
41
|
+
# @see https://github.com/sass/sass/blob/63586f1e51c0aec47a7afcc8cd17aa03f32f0a29/lib/sass/engine.rb#L764-765
|
42
|
+
#
|
43
|
+
# @return [Symbol] the type of Sass comment
|
44
|
+
def type
|
45
|
+
silent = @value[1] == ::Sass::Engine::SASS_COMMENT_CHAR
|
46
|
+
loud = !silent && @value[2] == ::Sass::Engine::SASS_LOUD_COMMENT_CHAR
|
47
|
+
if silent
|
48
|
+
:silent
|
49
|
+
elsif loud
|
50
|
+
:loud
|
51
|
+
else
|
52
|
+
:normal
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Representation of a number and a unit.
|
5
|
+
# The Sass equivalent is the {::Sass::Script::Value::Number}
|
6
|
+
# or {::Sass::Script::Tree::Literal} if the dimension is a part
|
7
|
+
# of an {ExpressionNode}'s {OperationNode}.
|
8
|
+
class DimensionNode < Node
|
9
|
+
attr_accessor :value
|
10
|
+
attr_accessor :unit
|
11
|
+
|
12
|
+
# @see Node#to_sass
|
13
|
+
def to_sass
|
14
|
+
numerator = @unit.is_a?(UnitNode) ? @unit.numerator : @unit['numerator']
|
15
|
+
denominator = @unit.is_a?(UnitNode) ? @unit.denominator : @unit['denominator']
|
16
|
+
dimension = ::Sass::Script::Value::Number.new(@value, numerator, denominator)
|
17
|
+
node(::Sass::Script::Tree::Literal.new(dimension), line)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@value.to_s + @unit.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Less2Sass
|
2
|
+
module Less
|
3
|
+
module Tree
|
4
|
+
# Represents a static CSS directive.
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
# - @charset
|
8
|
+
# - @namespace
|
9
|
+
# - @keyframes
|
10
|
+
# - @counter-style
|
11
|
+
# - @document
|
12
|
+
# - @supports
|
13
|
+
#
|
14
|
+
# The Sass equivalent is {::Sass::Tree::DirectiveNode}.
|
15
|
+
# @note Sits on a new line.
|
16
|
+
# @see Node
|
17
|
+
class DirectiveNode < Node
|
18
|
+
attr_accessor :name
|
19
|
+
attr_accessor :value
|
20
|
+
attr_accessor :rules
|
21
|
+
attr_accessor :index
|
22
|
+
attr_accessor :currentFileInfo
|
23
|
+
attr_accessor :debugInfo
|
24
|
+
attr_accessor :isRooted
|
25
|
+
|
26
|
+
# @return [::Sass::Tree::DirectiveNode]
|
27
|
+
# @see Node#to_sass
|
28
|
+
def to_sass
|
29
|
+
node = node(::Sass::Tree::DirectiveNode.new(value), line(:new))
|
30
|
+
@rules.rules.each { |c| node << c.to_sass } # Not all children should be passed
|
31
|
+
node
|
32
|
+
end
|
33
|
+
|
34
|
+
def value
|
35
|
+
@value.empty? ? [@name] : [@name + ' ', @value.to_s]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|