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,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'