less_to_sass 0.1.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.
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,32 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the elements of a {SelectorNode}.
5
+ # These elements are usually separated by a
6
+ # {CombinatorNode}.
7
+ #
8
+ # No equivalent in Sass.
9
+ class ElementNode < Node
10
+ # @return [Tree::CombinatorNode]
11
+ attr_accessor :combinator
12
+ # @return [String]
13
+ attr_accessor :value
14
+ # @return [Integer]
15
+ attr_accessor :index
16
+ # @return [Hash]
17
+ attr_accessor :currentFileInfo
18
+
19
+ # @see Node#to_sass
20
+ def to_sass
21
+ if @value.is_a?(VariableNode)
22
+ @value.to_sass
23
+ elsif @value.is_a?(String)
24
+ @combinator.to_s + @value
25
+ else
26
+ raise FeatureConversionError, self
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,73 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the expression in the Less AST.
5
+ #
6
+ # Sass does not have an Expression node. It is usually
7
+ # represented as the `expr` member of the {::Sass::Tree::VariableNode},
8
+ # that represents a variable definition.
9
+ #
10
+ # The Sass equivalent is either {::Sass::Script::Value::Base}
11
+ # wrapped in {::Sass::Script::Tree::Literal} or {::Sass::Tree::Node}.
12
+ class ExpressionNode < Node
13
+ attr_accessor :value
14
+
15
+ # @return [::Sass::Script::Tree::Literal, ::Sass::Script::Tree::ListLiteral, ::Sass::Tree::Node]
16
+ # @see Node#to_sass
17
+ def to_sass
18
+ if @value.is_a?(Array)
19
+ # TODO: document solution of: method to_sass is invoked on "to right":String, deal with it
20
+ if is_multiword_keyword?
21
+ multiword_keyword_argument
22
+ elsif should_be_literal?
23
+ @value.inject([]) { |value, elem| value << elem.to_s }.join(' ')
24
+ else
25
+ elements = @value.inject([]) do |value, elem|
26
+ node = elem.to_sass
27
+ node = node(::Sass::Script::Tree::Literal.new(node), line) if node.is_a?(::Sass::Script::Value::Base)
28
+ value << node
29
+ end
30
+ node(::Sass::Script::Tree::ListLiteral.new(elements, :space), line)
31
+ end
32
+ else
33
+ value = @value.to_sass
34
+ return value unless value.is_a?(::Sass::Script::Value::Base)
35
+ node(::Sass::Script::Tree::Literal.new(value), line)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ LITERAL_PROPERTIES = %w(font transition).freeze
42
+
43
+ # @todo: should be checked once more
44
+ def is_multiword_keyword?
45
+ @value.select { |elem| !elem.is_a?(KeywordNode) }.empty?
46
+ end
47
+
48
+ # Checks, whether the expression contains a {VariableNode}.
49
+ # Some properties in Less store their values as string literals
50
+ # instead of list literals.
51
+ #
52
+ # @return [Boolean] true if the expression should be converted
53
+ # to {::Sass::Script::Tree::Literal}
54
+ def should_be_literal?
55
+ grandparent = @parent.parent
56
+ return false unless grandparent.is_a?(RuleNode)
57
+ LITERAL_PROPERTIES.include?(grandparent.name.value) && !contains_variables?
58
+ end
59
+
60
+ # Creates a {::Sass::Script::Tree::ListLiteral} out of
61
+ # multiple keywords - usually referencing a complex CSS keyword.
62
+ #
63
+ # Example (`to right` is an example of multiword keyword):
64
+ # `list-style-image: linear-gradient(to right, rgba(255,0,0,0), rgba(255,0,0,1));`
65
+ #
66
+ def multiword_keyword_argument
67
+ keywords = @value.inject([]) { |value, elem| value << elem.to_sass }
68
+ node(::Sass::Script::Tree::ListLiteral.new(keywords, :space), line)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,14 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class ExtendNode < Node
5
+ attr_accessor :selector
6
+ attr_accessor :option
7
+ attr_accessor :index
8
+ attr_accessor :object_id
9
+ attr_accessor :parent_ids
10
+ attr_accessor :currentFileInfo
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class ImportNode < Node
5
+ attr_accessor :options
6
+ attr_accessor :index
7
+ attr_accessor :path
8
+ attr_accessor :features
9
+ attr_accessor :currentFileInfo
10
+ attr_accessor :css
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the CSS property names and
5
+ # a little bit more.
6
+ #
7
+ # It can represent the name of a CSS rule,
8
+ # or it can be a part of a variable definition's
9
+ # value.
10
+ #
11
+ # In the latter case its Sass equivalents are:
12
+ # - {::Sass::Script::Value::Bool}
13
+ # - {::Sass::Script::Value::Null}
14
+ class KeywordNode < Node
15
+ attr_accessor :value
16
+
17
+ def to_s
18
+ @value.to_s
19
+ end
20
+
21
+ def empty?
22
+ @value.empty?
23
+ end
24
+
25
+ # Returns a SassScript Value node.
26
+ #
27
+ # Usually will be called in case of a variable
28
+ # definition and its parent node would be a
29
+ # {ExpressionNode}, which would wrap it up into a
30
+ # {::Sass::Script::Tree::Literal}.
31
+ #
32
+ # @raise FeatureConversionError if this node's value is not expected.
33
+ # @return [::Sass::Script::Value::Base]
34
+ # @see Node#to_sass
35
+ def to_sass
36
+ case @value
37
+ when 'true' then ::Sass::Script::Value::Bool.new(true)
38
+ when 'false' then ::Sass::Script::Value::Bool.new(false)
39
+ when 'null' then ::Sass::Script::Value::Null.new
40
+ else
41
+ raise FeatureConversionError, self unless @value.respond_to?(:to_s)
42
+ string = ::Sass::Script::Value::String.new(@value.to_s)
43
+ node(::Sass::Script::Tree::Literal.new(string), line)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class MediaNode < Node
5
+ attr_accessor :index
6
+ attr_accessor :currentFileInfo
7
+ attr_accessor :features
8
+ attr_accessor :rules
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class MixinCallNode < Node
5
+ attr_accessor :selector
6
+ attr_accessor :arguments
7
+ attr_accessor :index
8
+ attr_accessor :currentFileInfo
9
+ attr_accessor :important
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class MixinDefinitionNode < Node
5
+ attr_accessor :name
6
+ attr_accessor :selectors
7
+ attr_accessor :params
8
+ attr_accessor :condition
9
+ attr_accessor :variadic
10
+ attr_accessor :arity
11
+ attr_accessor :rules
12
+ attr_accessor :_lookups
13
+ attr_accessor :required
14
+ attr_accessor :optionalParameters
15
+ attr_accessor :frames
16
+
17
+ # @see Node#creates_environment?
18
+ def creates_environment?
19
+ true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class NegativeNode < Node
5
+ attr_accessor :value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,212 @@
1
+ require 'less2sass/less/environment'
2
+
3
+ module Less2Sass
4
+ module Less
5
+ module Tree
6
+ # The base node class of the Less AST.
7
+ class Node
8
+ include Enumerable
9
+
10
+ # The parent node of this node.
11
+ #
12
+ # @return [Less2Sass::Less::Tree::Node]
13
+ attr_accessor :parent
14
+
15
+ # Whether or not this node has parent node.
16
+ #
17
+ # @return [Boolean]
18
+ attr_reader :has_parent
19
+
20
+ # The child nodes of this node.
21
+ #
22
+ # @return [Array<Less2Sass::Less::Tree::Node>]
23
+ attr_accessor :children
24
+
25
+ # Whether or not this node has child nodes.
26
+ #
27
+ # @return [Boolean]
28
+ attr_reader :has_children
29
+
30
+ # Sets the line number of the node
31
+ #
32
+ # @return [Void]
33
+ attr_writer :line
34
+
35
+ # This member gets set if the node's children contain
36
+ # referenced {VariableNode}s.
37
+ #
38
+ # @return [Array<String>] referenced variable names
39
+ attr_accessor :ref_vars
40
+
41
+ # The environment, where the nodes sits lexically.
42
+ # TODO: Consider to set a reference to the lexical scope (the environment) of the node.
43
+ # @return [Less2Sass::Less::Environment]
44
+ # attr_reader :env
45
+
46
+ # The current line number the conversion process is at
47
+ @@lines = 0
48
+
49
+ def initialize(parent)
50
+ @children = []
51
+ @parent = parent
52
+ @creates_new_line = false
53
+ end
54
+
55
+ # @private
56
+ def parent=(parent)
57
+ @has_parent ||= !parent.nil?
58
+ @parent = parent
59
+ end
60
+
61
+ # @private
62
+ def children=(children)
63
+ @has_children ||= !children.empty?
64
+ @children = children
65
+ end
66
+
67
+ # @private
68
+ def ref_vars
69
+ @ref_vars || get_referenced_variable_names
70
+ end
71
+
72
+ # Tells, whether the node creates a new context
73
+ # or variable environment.
74
+ #
75
+ # @return [Boolean]
76
+ def creates_context?
77
+ false
78
+ end
79
+
80
+ # Appends a child to the node.
81
+ #
82
+ # @param [Less2Sass::Less::Tree::Node, Array<Less2Sass::Less::Tree::Node>] child
83
+ # The child node or nodes
84
+ def <<(child)
85
+ return if child.nil?
86
+ if child.is_a?(Array)
87
+ child.each { |c| self << c }
88
+ else
89
+ @has_children = true
90
+ child.parent = self
91
+ @children << child
92
+ end
93
+ end
94
+
95
+ # Compares this node and another object (only other {Less2Sass::Less::Tree::Node}s will be equal).
96
+ #
97
+ # @param [Object] other The object to compare with
98
+ # @return [Boolean] Whether or not this node and the other object
99
+ # are the same
100
+ def ==(other)
101
+ self.class == other.class && other.children == children
102
+ end
103
+
104
+ # Iterates through each node in the tree rooted at this node
105
+ # in a pre-order walk.
106
+ #
107
+ # @yield node
108
+ # @yieldparam node [Less2Sass::Less::Tree::Node] a node in the tree
109
+ def each
110
+ yield self
111
+ children.each { |c| c.each { |n| yield n } }
112
+ end
113
+
114
+ # The base implementation of transform.
115
+ #
116
+ # Transforms the tree by its traversal. Each respective node type
117
+ # should implement the specific transformation and calling `#super`.
118
+ # The position, where `#super` is called determines the walking order.
119
+ # The standard should be post-order walk - transforming the child nodes
120
+ # first and self as last.
121
+ #
122
+ # Should be overridden by subclasses adding specific implementation.
123
+ #
124
+ # @param [Less2Sass::Less::Environment] env parent environment
125
+ # @return [Void]
126
+ def transform(env = nil)
127
+ # TODO: Consider to set a reference to the lexical scope (the environment) of the node.
128
+ # @env = env
129
+ @children.each { |c| c.transform(env) }
130
+ end
131
+
132
+ # Returns the line number and sets the class level
133
+ # current line number based on the passed option.
134
+ #
135
+ # @param [Symbol] opt the line number option
136
+ # @option opt [Symbol] :new tells the method to
137
+ # return the next line and increments @@lines
138
+ # @option opt [Symbol] :current tells the method to
139
+ # return the current line the converter is "processing"
140
+ #
141
+ # @return [Fixnum] the line number based on the given option
142
+ def line(opt = :current)
143
+ case opt
144
+ when :current
145
+ @@lines
146
+ when :new
147
+ @@lines = @@lines.next
148
+ else
149
+ raise StandardError, "Option can't be other than :current or :new"
150
+ end
151
+ end
152
+
153
+ # Converts this node to the equivalent Sass node
154
+ #
155
+ # Should be overwritten by subclasses.
156
+ # All subclasses should also set the line number
157
+ # of the resulting Sass node.
158
+ #
159
+ # @raise NotImplementedError if called on the base node object
160
+ # or subclass method not implemented, yet.
161
+ def to_sass
162
+ raise NotImplementedError
163
+ end
164
+
165
+ # Loops through the children nodes and
166
+ # searches for variable occurrences.
167
+ #
168
+ # @return [Array<String>] list of variable names
169
+ # contained in the value's expressions
170
+ # @note Use on {Less2Sass::Less::Tree::RuleNode}s. It's a good practice
171
+ # to check first if it's a variable definition.
172
+ # See (Less2Sass::Less::Tree::RuleNode#is_variable_definition?)
173
+ def get_referenced_variable_names
174
+ if is_a?(VariableNode)
175
+ [@name]
176
+ else
177
+ @children.inject([]) { |vars, c| vars + c.get_referenced_variable_names }
178
+ end
179
+ end
180
+
181
+ # Checks, whether a variable is referenced in the scope of the node.
182
+ #
183
+ # @return [Boolean]
184
+ def contains_variables?
185
+ variables = nil
186
+ each do |child|
187
+ if child.is_a?(VariableNode)
188
+ variables = true
189
+ break
190
+ end
191
+ end
192
+ variables
193
+ end
194
+
195
+ protected
196
+
197
+ # Sets up a node with the mandatory options (line, options).
198
+ #
199
+ # @param [::Sass::Tree::Node, ::Sass::Script::Tree::Node] node
200
+ # the node to be set up
201
+ # @param [Fixnum] line the line number, where the node sits lexically
202
+ # @param [Hash] options the options to pass to the node
203
+ # @return [::Sass::Tree::Node, ::Sass::Script::Tree::Node] the set up node
204
+ def node(node, line, options = { :style => :nested })
205
+ node.line = line if line
206
+ node.options = options if node.class.method_defined?(:options=)
207
+ node
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,63 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Node representing the operation with 2 operands.
5
+ #
6
+ # Converts to {::Sass::Script::Tree::Operation}.
7
+ class OperationNode < Node
8
+ attr_accessor :op
9
+ attr_accessor :operands
10
+ attr_accessor :isSpaced
11
+
12
+ def to_s
13
+ @operands[0].to_s + @op + @operands[1].to_s
14
+ end
15
+
16
+ # @see Node#to_sass
17
+ def to_sass
18
+ node(::Sass::Script::Tree::Operation.new(@operands[0].to_sass, @operands[1].to_sass, sass_operator), line)
19
+ end
20
+
21
+ # A hash from operator strings to the corresponding token types.
22
+ #
23
+ # Copied from Sass' lexer
24
+ # @see https://github.com/sass/sass/blob/stable/lib/sass/script/lexer.rb#L44-L69
25
+ OPERATORS = {
26
+ '+' => :plus,
27
+ '-' => :minus,
28
+ '*' => :times,
29
+ '/' => :div,
30
+ '%' => :mod,
31
+ '=' => :single_eq,
32
+ ':' => :colon,
33
+ '(' => :lparen,
34
+ ')' => :rparen,
35
+ ',' => :comma,
36
+ 'and' => :and,
37
+ 'or' => :or,
38
+ 'not' => :not,
39
+ '==' => :eq,
40
+ '!=' => :neq,
41
+ '>=' => :gte,
42
+ '<=' => :lte,
43
+ '>' => :gt,
44
+ '<' => :lt,
45
+ '#{' => :begin_interpolation,
46
+ '}' => :end_interpolation,
47
+ ';' => :semicolon,
48
+ '{' => :lcurly,
49
+ '...' => :splat
50
+ }.freeze
51
+
52
+ # Returns the sass operator symbol, that is used internally.
53
+ #
54
+ # @param [String] op the operator to look for
55
+ # @return [Symbol] the operator symbol
56
+ def sass_operator(op = @op)
57
+ raise Less2Sass::OperatorConversionError, op unless OPERATORS[op]
58
+ OPERATORS[op]
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,9 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class ParenNode < Node
5
+ attr_accessor :value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents a single or double-quoted string in Less.
5
+ #
6
+ # The Sass equivalent is a {::Sass::Script::Value::String}
7
+ # wrapped in {::Sass::Script::Tree::Literal}.
8
+ class QuotedNode < Node
9
+ attr_accessor :escaped
10
+ attr_accessor :value
11
+ attr_accessor :quote
12
+ attr_accessor :index
13
+ attr_accessor :currentFileInfo
14
+
15
+ # Returns the Sass equivalent for a quoted string.
16
+ #
17
+ # @return [::Sass::Script::Tree::Literal] simple string literal
18
+ # @return [::Sass::Script::Tree::StringInterpolation] interpolation node,
19
+ # if the string contains interpolations
20
+ # @see Node#to_sass
21
+ def to_sass
22
+ if string_interpolation?
23
+ interpolation_node(@value)
24
+ else
25
+ node(::Sass::Script::Tree::Literal.new(::Sass::Script::Value::String.new(@value, :string)), line)
26
+ end
27
+ end
28
+
29
+ # Returns the string's raw value without passing the type of quote.
30
+ #
31
+ # Sass has its own standards regarding the quotes, it should be in
32
+ # Sass' competences to choose what type of quote to use.
33
+ def to_s
34
+ @value
35
+ end
36
+
37
+ # Checks, whether the quoted string contains an interpolation.
38
+ #
39
+ # @return [Boolean]
40
+ def string_interpolation?(value = @value)
41
+ !value.index(INTERPOLATION).nil?
42
+ end
43
+
44
+ private
45
+
46
+ INTERPOLATION = /(@\{[\w_-]*\})/
47
+ VARIABLE_NAME = /@\{([\w_-]*)\}/
48
+
49
+ # Creates a {::Sass::Script::Tree::StringInterpolation} node.
50
+ #
51
+ # @param [String] value the string containing interpolation(s)
52
+ # @return [::Sass::Script::Tree::StringInterpolation] the interpolation node
53
+ def interpolation_node(value)
54
+ parts = value.split(INTERPOLATION, 2) # Must be split maximally into 3 parts
55
+ parts[2] = '' if parts.length == 2 # Always must have 3 parts
56
+ before = node(::Sass::Script::Tree::Literal.new(::Sass::Script::Value::String.new(parts[0], :string)), line)
57
+ mid = node(::Sass::Script::Tree::Variable.new(parts[1].match(VARIABLE_NAME)[1]), line)
58
+ after = string_interpolation?(parts[2]) ? interpolation_node(parts[2]) : node(::Sass::Script::Tree::Literal.new(::Sass::Script::Value::String.new(parts[2], :string)), line)
59
+ node(::Sass::Script::Tree::StringInterpolation.new(before, mid, after), line)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end