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,119 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # A RuleNode in Less can be a CSS rule, or a
5
+ # variable definition.
6
+ #
7
+ # Examples:
8
+ # - color: @var;
9
+ # - @var: 50px;
10
+ #
11
+ # The Sass equivalent is {::Sass::Tree::VariableNode}
12
+ # or {::Sass::Tree::RuleNode}.
13
+ class RuleNode < Node
14
+ attr_accessor :name
15
+ attr_accessor :value
16
+ attr_accessor :important
17
+ attr_accessor :merge
18
+ attr_accessor :index
19
+ attr_accessor :currentFileInfo
20
+ attr_accessor :inline
21
+ attr_accessor :variable
22
+
23
+ attr_accessor :guarded
24
+ attr_accessor :global
25
+
26
+ # @see VariableNode#sass_name
27
+ def sass_name
28
+ @name[1..-1] if @name
29
+ end
30
+
31
+ # Returns whether the node is a variable definition.
32
+ #
33
+ # @return [Boolean]
34
+ def is_variable_definition?
35
+ @variable
36
+ end
37
+
38
+ # Checks, if the given variable is referenced by the variable's definition
39
+ #
40
+ # @param [Less2Sass::Less::Tree:RuleNode] variable the variable
41
+ # @param [String] variable the variable's name
42
+ # @return [Boolean]
43
+ def references?(variable)
44
+ if variable.is_a?(String)
45
+ @ref_vars.include?(variable)
46
+ else
47
+ @ref_vars.include?(variable.name)
48
+ end
49
+ end
50
+
51
+ # Converts this node to {::Sass::Tree::VariableNode}, if
52
+ # it's a variable definition. Otherwise it must be a CSS
53
+ # property declaration and gets converted to {::Sass::Tree::PropNode}.
54
+ #
55
+ # The 3rd param of `::Sass::Tree::PropNode` refers to the CSS
56
+ # syntax to be outputted. Its value can be:
57
+ # - `:new` using `a: b`-style syntax
58
+ # - `:old` using `:a b`-style syntax
59
+ #
60
+ # For the sake of simplicity, we'll use the new syntax only.
61
+ # Later could be set in cli arguments as option.
62
+ #
63
+ # @note Always put on a new line.
64
+ # @return [::Sass::Tree::VariableNode, ::Sass::Tree::PropNode]
65
+ # @see Node#to_sass
66
+ def to_sass
67
+ node ||=
68
+ begin
69
+ if is_variable_definition?
70
+ ::Sass::Tree::VariableNode.new(
71
+ sass_name, @value.to_sass, @guarded, @global
72
+ )
73
+ else
74
+ # TODO: Don't forget about the `#{}`
75
+ ::Sass::Tree::PropNode.new(
76
+ process_property_name, @value.to_sass, :new
77
+ )
78
+ end
79
+ end
80
+ node(node, line(:new))
81
+ end
82
+
83
+ private
84
+
85
+ # Creates the Sass representation of the property name.
86
+ #
87
+ # @return [Array<String, ::Sass::Script::Tree::Node>]
88
+ def process_property_name
89
+ return [create_name] unless @name.is_a?(Array)
90
+ @name.inject([]) do |name, part|
91
+ name << create_name(part)
92
+ end
93
+ end
94
+
95
+ # Returns a part of the property name.
96
+ #
97
+ # @param [VariableNode, #to_s] name part of the property name
98
+ # @return [String, ::Sass::Script::Tree::Node] converted
99
+ # part of property name
100
+ def create_name(name = @name)
101
+ if name.is_a?(VariableNode)
102
+ create_interpolation(name)
103
+ else
104
+ name.to_s
105
+ end
106
+ end
107
+
108
+ # Creates a Sass interpolation node
109
+ #
110
+ # @param [VariableNode] variable_node the variable node to convert
111
+ # @return [::Sass::Script::Tree::Interpolation] a Sass Interpolation node
112
+ def create_interpolation(variable_node)
113
+ var = ::Sass::Script::Tree::Variable.new(variable_node.name[1..-1])
114
+ node(::Sass::Script::Tree::Interpolation.new(nil, var, nil, false, false), line)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,9 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class RulesetCallNode < Node
5
+ attr_accessor :variable
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # The Ruleset node is the root node of every Less AST.
5
+ # It also means a block of selector and its inner rules.
6
+ #
7
+ # Its Sass equivalent is a {::Sass::Tree::RootNode}, or
8
+ # {::Sass::Tree::RuleNode}.
9
+ class RulesetNode < Node
10
+ attr_accessor :selectors, :rules, :_lookups,
11
+ :root, :firstRoot, :strictImports
12
+
13
+ def initialize(options = nil)
14
+ super
15
+ @options = options unless options.nil?
16
+ end
17
+
18
+ # @see Node#creates_context?
19
+ def creates_context?
20
+ true
21
+ end
22
+
23
+ # Transforms the ruleset node by reordering the child nodes.
24
+ #
25
+ # 1. Creates a new environment and puts the child
26
+ # nodes into it.
27
+ #
28
+ # 2. Further traverses the tree of nodes and transforms them, as well.
29
+ #
30
+ # 3. Saves the re-ordered rules and child nodes.
31
+ #
32
+ # @param (see Node#transform)
33
+ # @return [Void]
34
+ def transform(env = nil)
35
+ env = Less2Sass::Less::Environment.new(env)
36
+ env.set_environment(@children)
37
+ env.build
38
+ super(env)
39
+ @rules = env.get_ordered_child_nodes
40
+ @children = @rules
41
+ end
42
+
43
+ # Converts node to {::Sass::Tree::RootNode} if it's root.
44
+ # Otherwise it converts to {::Sass::Tree::RuleNode}.
45
+ #
46
+ # In case of root node, all the children nodes have to be
47
+ # converted. Otherwise rule nodes only.
48
+ #
49
+ # @note I both cases the node is placed on a new line.
50
+ # @return [::Sass::Tree::RootNode, ::Sass::Tree::RuleNode]
51
+ # @see Node#to_sass
52
+ def to_sass(options = nil)
53
+ if @root
54
+ node = node(::Sass::Engine.new('', options).to_tree, line(:new))
55
+ children = @children
56
+ else
57
+ node = node(::Sass::Tree::RuleNode.new(get_full_selector), line(:new))
58
+ children = @rules.is_a?(Array) ? @rules : [@rules]
59
+ end
60
+ children.each { |c| node << c.to_sass }
61
+ node
62
+ end
63
+
64
+ private
65
+
66
+ # Creates a list of selectors.
67
+ #
68
+ # @return [Array<String, Sass::Script::Tree::Node>] the list of selectors
69
+ def get_full_selector
70
+ raise UnknownError if @selectors.nil?
71
+ if @selectors.is_a?(Array)
72
+ # Creates a list of selectors {Array<String, Sass::Script::Tree::node>} separated by a comma
73
+ # and returns the list without the last, trailing comma
74
+ @selectors.inject([]) { |rule, selector| rule << [selector.to_sass, ','] }.flatten[0..-2]
75
+ else
76
+ [@selectors.to_sass].flatten
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,27 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the full selector of a given {RulesetNode}.
5
+ #
6
+ # No equivalent node is present in Sass, it's the `rule`
7
+ # member of {::Sass::Tree::PropNode}, instead.
8
+ class SelectorNode < Node
9
+ # @return [Array<Tree::ElementNode>]
10
+ attr_accessor :elements
11
+ attr_accessor :extendList
12
+ attr_accessor :condition
13
+ # @return [Hash]
14
+ attr_accessor :currentFileInfo
15
+ # @return [Boolean]
16
+ attr_accessor :evaldCondition
17
+
18
+ # see Node#to_sass
19
+ def to_sass
20
+ raise UnknownError if @elements.nil?
21
+ return @elements.to_sass unless @elements.is_a?(Array)
22
+ @elements.inject([]) { |selector, element| selector << element.to_sass }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class UnicodeDescriptorNode < Node
5
+ attr_accessor :value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ class UnitNode < Node
5
+ attr_accessor :numerator
6
+ attr_accessor :denominator
7
+ attr_accessor :backupUnit
8
+
9
+ def to_s
10
+ val = @numerator[0].to_s
11
+ val += @denominator[0] unless @denominator.nil? || @denominator.empty?
12
+ val
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the CSS `url` function call.
5
+ #
6
+ # The Sass equivalent is {::Sass::Script::Tree::Funcall}.
7
+ class UrlNode < Node
8
+ attr_accessor :value
9
+ attr_accessor :currentFileInfo
10
+ attr_accessor :index
11
+ attr_accessor :isEvald
12
+
13
+ # @return [::Sass::Script::Tree::Funcall]
14
+ # @see https://github.com/sass/sass/blob/stable/lib/sass/script/tree/funcall.rb#L42-46
15
+ # @see Node#to_sass
16
+ def to_sass
17
+ node(::Sass::Script::Tree::Funcall.new('url', [@value.to_sass], ::Sass::Util::NormalizedMap.new, nil, nil), line)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,53 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # Represents the value of any Less AST node.
5
+ #
6
+ # It has no individual representation in Sass,
7
+ # so it returns its value node's Sass representation,
8
+ # when {#to_sass} gets called.
9
+ #
10
+ # Sass equivalents:
11
+ # - {::Sass::Script::Tree::Literal} if part of property declaration
12
+ # - {::Sass::Script::Tree::ListLiteral} if part of variable definition
13
+ #
14
+ # @see Node
15
+ class ValueNode < Node
16
+ # @todo update Sass and check out a lately commit regarding the deprecated SassScript usage in CSS rules
17
+ # @see https://github.com/sass/sass/commit/b671122234d2d4df088a6c6d4c54b64d6997d255
18
+
19
+ # The ValueNode is not the final value of a variable
20
+ # or a property.
21
+ #
22
+ # @return [Node, Array<Node>]
23
+ attr_accessor :value
24
+
25
+ # This node's value is either a single expression,
26
+ # or an array of those. The array needs to be further
27
+ # processed.
28
+ #
29
+ # @return [::Sass::Script::Tree::Literal, ::Sass::Script::Tree::ListLiteral]
30
+ # @see Node#to_sass
31
+ def to_sass
32
+ if @value.is_a?(Array)
33
+ # TODO: don't forget about interpolation. What if one of the expressions is a variable?
34
+ if @parent.is_variable_definition? || contains_variables?
35
+ elements = @value.inject([]) { |elements, elem| elements << elem.to_sass }
36
+ node(::Sass::Script::Tree::ListLiteral.new(elements, :comma), line)
37
+ else
38
+ value = @value.inject([]) do |value, elem|
39
+ substring = elem.to_sass
40
+ # TODO: describe solution for '"Source Code Pro", Monaco, monospace'
41
+ substring = substring.value.value if substring.is_a?(::Sass::Script::Tree::Literal)
42
+ value << substring
43
+ end.join(', ')
44
+ node(::Sass::Script::Tree::Literal.new(::Sass::Script::Value::String.new(value)), line)
45
+ end
46
+ else
47
+ @value.to_sass
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,43 @@
1
+ module Less2Sass
2
+ module Less
3
+ module Tree
4
+ # A node representing a variable.
5
+ # The Sass representation of it is {::Sass::Script::Tree:Variable}
6
+ class VariableNode < Node
7
+ # @return [String]
8
+ attr_accessor :name
9
+ # @return [Integer]
10
+ attr_accessor :index
11
+ # @return [Hash]
12
+ attr_accessor :currentFileInfo
13
+
14
+ # Returns the sass specific name property by
15
+ # removing the starting @.
16
+ #
17
+ # Example:
18
+ # node.sass_name => "var1"
19
+ #
20
+ # @return [String]
21
+ def sass_name
22
+ @name[1..-1] if @name
23
+ end
24
+
25
+ # Checks, whether the variable node is an interpolation
26
+ #
27
+ # @return [Boolean]
28
+ def interpolation?
29
+ return false unless [ElementNode, RuleNode].include?(@parent.class)
30
+ return !@parent.is_variable_definition? if @parent.is_a?(RuleNode)
31
+ true
32
+ end
33
+
34
+ # @see Node#to_sass
35
+ def to_sass
36
+ variable = node(::Sass::Script::Tree::Variable.new(sass_name), line)
37
+ variable = node(::Sass::Script::Tree::Interpolation.new(nil, variable, nil, false, false), line) if interpolation?
38
+ variable
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,34 @@
1
+ require 'less2sass/less/tree/node'
2
+ require 'less2sass/less/tree/alpha_node'
3
+ require 'less2sass/less/tree/anonymous_node'
4
+ require 'less2sass/less/tree/assignment_node'
5
+ require 'less2sass/less/tree/attribute_node'
6
+ require 'less2sass/less/tree/call_node'
7
+ require 'less2sass/less/tree/color_node'
8
+ require 'less2sass/less/tree/combinator_node'
9
+ require 'less2sass/less/tree/comment_node'
10
+ require 'less2sass/less/tree/condition_node'
11
+ require 'less2sass/less/tree/detached_ruleset_node'
12
+ require 'less2sass/less/tree/dimension_node'
13
+ require 'less2sass/less/tree/directive_node'
14
+ require 'less2sass/less/tree/element_node'
15
+ require 'less2sass/less/tree/expression_node'
16
+ require 'less2sass/less/tree/extend_node'
17
+ require 'less2sass/less/tree/import_node'
18
+ require 'less2sass/less/tree/keyword_node'
19
+ require 'less2sass/less/tree/media_node'
20
+ require 'less2sass/less/tree/mixin_call_node'
21
+ require 'less2sass/less/tree/mixin_definition_node'
22
+ require 'less2sass/less/tree/negative_node'
23
+ require 'less2sass/less/tree/operation_node'
24
+ require 'less2sass/less/tree/paren_node'
25
+ require 'less2sass/less/tree/quoted_node'
26
+ require 'less2sass/less/tree/rule_node'
27
+ require 'less2sass/less/tree/ruleset_call_node'
28
+ require 'less2sass/less/tree/ruleset_node'
29
+ require 'less2sass/less/tree/selector_node'
30
+ require 'less2sass/less/tree/unicode_descriptor_node'
31
+ require 'less2sass/less/tree/unit_node'
32
+ require 'less2sass/less/tree/url_node'
33
+ require 'less2sass/less/tree/value_node'
34
+ require 'less2sass/less/tree/variable_node'
@@ -0,0 +1,2 @@
1
+ require 'less2sass/less/tree'
2
+ require 'less2sass/less/ast_handler'
@@ -0,0 +1,57 @@
1
+ module Less2Sass
2
+ module Sass
3
+ # Wrapped Sass AST
4
+ class ASTHandler
5
+ # Creates an empty Sass AST.
6
+ #
7
+ # @param [::Sass::Tree::Node] tree the Sass AST
8
+ def initialize(tree)
9
+ @tree = tree
10
+ end
11
+
12
+ # Appends child notes to the empty Sass AST.
13
+ #
14
+ # Note, that this should be called only once.
15
+ #
16
+ # @param [::Sass::Tree::Node] child the rest
17
+ # of the converted Less AST
18
+ def <<(child)
19
+ # TODO: check if child.is_a?(::Sass::Tree::Node) and raise some error, if not
20
+ @tree << child
21
+ self
22
+ end
23
+
24
+ def to_css
25
+ @tree.render
26
+ end
27
+
28
+ # Outputs the transformed and converted AST
29
+ # in the form of Sass code.
30
+ #
31
+ # @todo method cannot handle import nodes, yet - single files only
32
+ #
33
+ # @param [String, IO] destination the base directory
34
+ # or an IO Stream where the converted projects
35
+ # should be outputted.
36
+ def code_gen(destination = nil)
37
+ code = @tree.to_scss
38
+ return code unless destination
39
+ if destination.is_a?(String)
40
+ open_file(destination, 'w') { |file| file.write(code) }
41
+ else
42
+ destination.write(code)
43
+ end
44
+ end
45
+
46
+ def open_file(filename, flag = 'r')
47
+ return if filename.nil?
48
+ return File.open(filename, flag) unless block_given?
49
+ yield File.open(filename, flag)
50
+ end
51
+
52
+ def to_yaml
53
+ @tree.to_yaml
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ module Less2Sass
2
+ module Sass
3
+ class Parser
4
+ def initialize(input)
5
+ @input = input
6
+ end
7
+
8
+ def parse
9
+ sass_file_content = File.read(@input)
10
+ sass_file_path = File.expand_path(@input)
11
+ tree = ::Sass::Engine.new(
12
+ sass_file_content,
13
+ :syntax => :scss, :filename => sass_file_path
14
+ ).to_tree
15
+ ::Sass::Tree::Visitors::CheckNesting.visit(tree)
16
+ ::Sass::Tree::Visitors::Perform.visit(tree)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ require 'less2sass/sass/ast_handler'
2
+ require 'sass'
@@ -0,0 +1,36 @@
1
+ module Less2Sass
2
+ module Util
3
+ extend self
4
+
5
+ # Returns a file's path relative to the Less2Sass root directory.
6
+ #
7
+ # @param file [String] The filename relative to the Less2Sass root
8
+ # @return [String] The filename relative to the the working directory
9
+ def scope(file)
10
+ File.join(Less2Sass::ROOT_DIR, file)
11
+ end
12
+
13
+ # Formats a multiline console input, so it can be passed as an argument
14
+ # to a console application.
15
+ #
16
+ # @return [String] properly formatted multiline argument
17
+ def read_stdin_as_multiline_arg
18
+ str = ''
19
+ puts 'Write your code below, so it can be converted for you.'
20
+ while line = gets
21
+ line["\n"] = "\\\n"
22
+ str << line
23
+ end
24
+ str
25
+ end
26
+
27
+ # Formats class names properly.
28
+ #
29
+ # @return [String] formatted class name
30
+ # @example formats dashed class name
31
+ # "anonymous_node".classify #=> "AnonymousNode"
32
+ def classify(str)
33
+ str.split('_').collect!(&:capitalize).join
34
+ end
35
+ end
36
+ end
data/lib/less2sass.rb ADDED
@@ -0,0 +1,9 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
3
+
4
+ require 'less2sass/constants'
5
+ require 'less2sass/error'
6
+ require 'less2sass/exec'
7
+ require 'less2sass/util'
8
+ require 'less2sass/sass'
9
+ require 'less2sass/less'