less_to_sass 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +70 -0
  4. data/bin/less2sass +12 -0
  5. data/bin/sass2less +12 -0
  6. data/lib/less2sass/constants.rb +5 -0
  7. data/lib/less2sass/error.rb +100 -0
  8. data/lib/less2sass/exec/base.rb +83 -0
  9. data/lib/less2sass/exec/conversion.rb +102 -0
  10. data/lib/less2sass/exec.rb +2 -0
  11. data/lib/less2sass/js/less_parser.js +48 -0
  12. data/lib/less2sass/less/ast_handler.rb +33 -0
  13. data/lib/less2sass/less/environment.rb +212 -0
  14. data/lib/less2sass/less/parser.rb +131 -0
  15. data/lib/less2sass/less/tree/alpha_node.rb +9 -0
  16. data/lib/less2sass/less/tree/anonymous_node.rb +43 -0
  17. data/lib/less2sass/less/tree/assignment_node.rb +10 -0
  18. data/lib/less2sass/less/tree/attribute_node.rb +11 -0
  19. data/lib/less2sass/less/tree/call_node.rb +65 -0
  20. data/lib/less2sass/less/tree/color_node.rb +27 -0
  21. data/lib/less2sass/less/tree/combinator_node.rb +18 -0
  22. data/lib/less2sass/less/tree/comment_node.rb +58 -0
  23. data/lib/less2sass/less/tree/condition_node.rb +13 -0
  24. data/lib/less2sass/less/tree/detached_ruleset_node.rb +10 -0
  25. data/lib/less2sass/less/tree/dimension_node.rb +26 -0
  26. data/lib/less2sass/less/tree/directive_node.rb +40 -0
  27. data/lib/less2sass/less/tree/element_node.rb +32 -0
  28. data/lib/less2sass/less/tree/expression_node.rb +73 -0
  29. data/lib/less2sass/less/tree/extend_node.rb +14 -0
  30. data/lib/less2sass/less/tree/import_node.rb +14 -0
  31. data/lib/less2sass/less/tree/keyword_node.rb +49 -0
  32. data/lib/less2sass/less/tree/media_node.rb +12 -0
  33. data/lib/less2sass/less/tree/mixin_call_node.rb +13 -0
  34. data/lib/less2sass/less/tree/mixin_definition_node.rb +24 -0
  35. data/lib/less2sass/less/tree/negative_node.rb +9 -0
  36. data/lib/less2sass/less/tree/node.rb +212 -0
  37. data/lib/less2sass/less/tree/operation_node.rb +63 -0
  38. data/lib/less2sass/less/tree/paren_node.rb +9 -0
  39. data/lib/less2sass/less/tree/quoted_node.rb +64 -0
  40. data/lib/less2sass/less/tree/rule_node.rb +119 -0
  41. data/lib/less2sass/less/tree/ruleset_call_node.rb +9 -0
  42. data/lib/less2sass/less/tree/ruleset_node.rb +82 -0
  43. data/lib/less2sass/less/tree/selector_node.rb +27 -0
  44. data/lib/less2sass/less/tree/unicode_descriptor_node.rb +9 -0
  45. data/lib/less2sass/less/tree/unit_node.rb +17 -0
  46. data/lib/less2sass/less/tree/url_node.rb +22 -0
  47. data/lib/less2sass/less/tree/value_node.rb +53 -0
  48. data/lib/less2sass/less/tree/variable_node.rb +43 -0
  49. data/lib/less2sass/less/tree.rb +34 -0
  50. data/lib/less2sass/less.rb +2 -0
  51. data/lib/less2sass/sass/ast_handler.rb +57 -0
  52. data/lib/less2sass/sass/parser.rb +20 -0
  53. data/lib/less2sass/sass.rb +2 -0
  54. data/lib/less2sass/util.rb +36 -0
  55. data/lib/less2sass.rb +9 -0
  56. 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,9 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class AlphaNode < Node
5
+ attr_accessor :value
6
+ end
7
+ end
8
+ end
9
+ 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,10 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class AssignmentNode < Node
5
+ attr_accessor :key
6
+ attr_accessor :value
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class AttributeNode < Node
5
+ attr_accessor :key
6
+ attr_accessor :op
7
+ attr_accessor :value
8
+ end
9
+ end
10
+ end
11
+ 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,13 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class ConditionNode < Node
5
+ attr_accessor :op
6
+ attr_accessor :lvalue
7
+ attr_accessor :rvalue
8
+ attr_accessor :index
9
+ attr_accessor :negate
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class DetachedRulesetNode < Node
5
+ attr_accessor :ruleset
6
+ attr_accessor :frames
7
+ end
8
+ end
9
+ end
10
+ 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