[Sass] [Less] Add a very basic Less converter.

This commit is contained in:
Nathan Weizenbaum 2010-04-26 13:58:06 -07:00
parent ab244176a9
commit fd0f2b45f1
3 changed files with 212 additions and 2 deletions

View File

@ -269,6 +269,7 @@ OPTS
list.exclude('lib/haml/helpers/xss_mods.rb')
list.exclude('lib/sass/plugin/merb.rb')
list.exclude('lib/sass/plugin/rails.rb')
list.exclude('lib/sass/less.rb')
end.to_a
t.options << '--incremental' if Rake.application.top_level_tasks.include?('redoc')
t.options += FileList.new(scope('yard/*.rb')).to_a.map {|f| ['-e', f]}.flatten

View File

@ -595,12 +595,12 @@ Options:
END
opts.on('-F', '--from FORMAT',
'The format to convert from. Can be css, scss, sass, or sass2.',
'The format to convert from. Can be css, scss, sass, less, or sass2.',
'sass2 is the same as sass, but updates more old syntax to new.',
'By default, this is inferred from the input filename.',
'If there is none, defaults to css.') do |name|
@options[:from] = name.downcase.to_sym
unless [:css, :scss, :sass, :sass2].include?(@options[:from])
unless [:css, :scss, :sass, :less, :sass2].include?(@options[:from])
raise "Unknown format for sass-convert --from: #{name}"
end
end
@ -715,6 +715,7 @@ END
case input.path
when /\.scss$/; :scss
when /\.sass$/; :sass
when /\.less$/; :less
when /\.css$/; :css
end
elsif @options[:in_place]
@ -743,6 +744,9 @@ END
if @options[:from] == :css
require 'sass/css'
::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
elsif @options[:from] == :less
require 'sass/less'
Less::Engine.new(input.read).to_tree.to_sass_tree.send("to_#{@options[:to]}", @options[:for_tree])
else
if input.is_a?(File)
::Sass::Files.tree_for(input.path, @options[:for_engine])

205
lib/sass/less.rb Executable file
View File

@ -0,0 +1,205 @@
#!/usr/bin/env ruby
require 'less'
module Less
module StyleSheet
module Mixin4
def build(env)
selectors.build(env, :mixin).each do |path|
el = path.inject(env.root) do |current, node|
current.descend(node.selector, node) or raise MixinNameError, "#{selectors.text_value} in #{env}"
end
if el.is_a?(Node::Mixin::Def)
env << Node::Mixin::Call.new(el, [], env)
else
sel = path.map {|e| e.sass_selector_str}.join(' ').gsub(' :', ':')
env << Node::Mixin::Extend.new(sel)
end
end
end
end
module Accessor1
def build(env)
warn <<WARNING
Sass doesn't support attribute accessors.
Ignoring #{text_value}
WARNING
Node::Anonymous.new("/* #{text_value} */")
end
end
module Entity::Alpha1
def build(env)
Node::Function.new("alpha",
[Node::Expression.new([
Node::Keyword.new("opacity"),
Node::Operator.new("="),
variable.build])])
end
end
end
module Node
class Element
def to_sass_tree
if root?
root = Sass::Tree::RootNode.new("")
rules.each {|r| root << r.to_sass_tree}
return root
end
sel =
case @selector
when /[+>~]/; "#{@selector} "
else @selector
end
rule = Sass::Tree::RuleNode.new([sel, @name])
rules.each {|r| rule << r.to_sass_tree}
return rule
end
def sass_selector_str
case @selector
when /[+>~]/; "#{@selector} #{@name}"
else @selector + @name
end
end
end
module Mixin
class Extend
include Entity
def initialize(name)
@name = name
end
def to_sass_tree
Sass::Tree::ExtendNode.new([@name])
end
end
class Call
def to_sass_tree
Sass::Tree::MixinNode.new(@mixin.name.gsub(/^\./, ''), @params.map {|v| v.to_sass_tree})
end
end
class Def
def to_sass_tree
mixin = Sass::Tree::MixinDefNode.new(name, @params.map do |v|
[Sass::Script::Variable.new(v), v.value.to_sass_tree]
end)
rules.each {|r| mixin << r.to_sass_tree}
mixin
end
end
end
class Property
def to_sass_tree
Sass::Tree::PropNode.new([self], @value.to_sass_tree, :new)
end
end
class Expression
def to_sass_tree
if first.is_a?(Array)
val = map {|e| _to_sass_tree(e)}.inject(nil) do |e, i|
next i unless e
Sass::Script::Operation.new(e, i, :comma)
end
else
val = _to_sass_tree(self)
end
val.options = {}
val
end
private
LESS_TO_SASS_OPERATORS = {"-" => :minus, "+" => :plus, "*" => :times, "/" => :div, "=" => :single_eq}
def _to_sass_tree(arr)
return Sass::Script::UnaryOperation.new(_to_sass_tree(arr[1..-1]), :minus) if arr[0] == "-"
first, rest = _sass_split(arr)
return first if rest.empty?
if rest[0].is_a?(Operator)
return Sass::Script::Operation.new(first, _to_sass_tree(rest[1..-1]),
LESS_TO_SASS_OPERATORS[rest[0]])
end
Sass::Script::Operation.new(first, _to_sass_tree(rest), :concat)
end
def _sass_split(arr)
return arr[0].to_sass_tree, arr[1..-1] unless arr[0] == "("
parens = 1
i = arr[1..-1].each_with_index do |e, i|
parens += 1 if e == "("
parens -= 1 if e == ")"
break i if parens == 0
end
return _to_sass_tree(arr[1...i+1]), arr[i+2..-1]
end
end
class Color
def to_sass_tree
Sass::Script::Color.new(:red => r, :green => g, :blue => b, :alpha => a)
end
end
class Number
def to_sass_tree
Sass::Script::Number.new(self, [self.unit])
end
end
class Variable
def to_sass_tree
if @declaration
Sass::Tree::VariableNode.new(self, @value.to_sass_tree, false)
else
Sass::Script::Variable.new(self)
end
end
end
class Function
def to_sass_tree
Sass::Script::Funcall.new(self, @args.map {|a| a.to_sass_tree})
end
end
class Keyword
def to_sass_tree
Sass::Script::String.new(self)
end
end
class Anonymous
def to_sass_tree
Sass::Script::String.new(self)
end
end
class Quoted
def to_sass_tree
Sass::Script::String.new(self, true)
end
end
class FontFamily
def to_sass_tree
@family.map {|f| f.to_sass_tree}.inject(nil) do |e, f|
next f unless e
Sass::Script::Operation.new(e, f, :comma)
end
end
end
end
end