class Sass::Tree::Visitors::ToCss
重写属性解析to_css.rb
A visitor for converting a Sass tree into CSS.
Attributes
source_mapping[R]
The source mapping for the generated CSS file. This is only set if ‘build_source_mapping` is passed to the constructor and {Sass::Engine#render} has been run.
Public Class Methods
new(build_source_mapping = false)
click to toggle source
@param build_source_mapping [Boolean] Whether to build a
\{Sass::Source::Map} while creating the CSS output. The mapping will be available from \{#source\_mapping} after the visitor has completed.
# File lib/sassmagic/reset.rb, line 205 def initialize(build_source_mapping = false) @tabs = 0 @line = 1 @offset = 1 @result = "" @source_mapping = Sass::Source::Map.new if build_source_mapping end
Public Instance Methods
visit(node)
click to toggle source
Runs the visitor on ‘node`.
@param node [Sass::Tree::Node] The root node of the tree to convert to CSS> @return [String] The CSS output.
Calls superclass method
# File lib/sassmagic/reset.rb, line 217 def visit(node) super rescue Sass::SyntaxError => e e.modify_backtrace(:filename => node.filename, :line => node.line) raise e end
Protected Instance Methods
erase!(chars)
click to toggle source
Move the output cursor back ‘chars` characters.
# File lib/sassmagic/reset.rb, line 249 def erase!(chars) return if chars == 0 str = @result.slice!(-chars..-1) newlines = str.count("\n") if newlines > 0 @line -= newlines @offset = @result[@result.rindex("\n") || 0..-1].size else @offset -= chars end end
for_node(node, attr_prefix = nil) { || ... }
click to toggle source
Associate all output produced in a block with a given node. Used for source mapping.
# File lib/sassmagic/reset.rb, line 235 def for_node(node, attr_prefix = nil) return yield unless @source_mapping start_pos = Sass::Source::Position.new(@line, @offset) yield range_attr = attr_prefix ? :"#{attr_prefix}_source_range" : :source_range return if node.invisible? || !node.send(range_attr) source_range = node.send(range_attr) target_end_pos = Sass::Source::Position.new(@line, @offset) target_range = Sass::Source::Range.new(start_pos, target_end_pos, nil) @source_mapping.add(source_range, target_range) end
lstrip() { || ... }
click to toggle source
lstrip the first output in the given block.
# File lib/sassmagic/reset.rb, line 291 def lstrip old_lstrip = @lstrip @lstrip = true yield ensure @lstrip = @lstrip && old_lstrip end
output(s)
click to toggle source
Add ‘s` to the output string and update the line and offset information accordingly.
# File lib/sassmagic/reset.rb, line 268 def output(s) if @lstrip s = s.gsub(/\A\s+/, "") @lstrip = false end newlines = s.count(NEWLINE) if newlines > 0 @line += newlines @offset = s[s.rindex(NEWLINE)..-1].size else @offset += s.size end @result << s end
prepend!(prefix)
click to toggle source
Prepend ‘prefix` to the output string.
# File lib/sassmagic/reset.rb, line 300 def prepend!(prefix) @result.insert 0, prefix return unless @source_mapping line_delta = prefix.count("\n") offset_delta = prefix.gsub(/.*\n/, '').size @source_mapping.shift_output_offsets(offset_delta) @source_mapping.shift_output_lines(line_delta) end
pxtorem(node,str,rem,ignore)
click to toggle source
# File lib/sassmagic/reset.rb, line 507 def pxtorem(node,str,rem,ignore) ret = str ignore = ignore || [] # 过滤掉不需要处理的name isNeedChangeValue = true; ignore.each{|v| # debugger if $classignore && (node.resolved_name.include? v) isNeedChangeValue = false end } if isNeedChangeValue #字符串替换px #'0 1px.jpg 1px 2px;3pxabc 4px'.scan(/([\.\d]+)+(px)+([;\s]+|$)/) #=> [["1", "px", " "], ["2", "px", ";"], ["4", "px", ""]] # str.scan(/([\.\d]+)+(px)+([;\s]+|$)/i){ |c,v| # if !(ignore.include? c+v) && (c != '1') && c =~ /^[^0]+/ # ret = ret.gsub(c.to_s,(format("%.3f",c.to_f/rem).to_f).to_s).gsub(v,'rem') # else # # debugger # ret = ret.gsub(/^0+/,'0').gsub(/(^0+)(\d+)/,'\2') # # ret = ret.to_s.gsub(/(^0+)(\d+)/,'\2') # end # } #修复bug '1px 1em' =>'.xrem .xem' str.scan(/(([\.\d]+)+(px))+([;\s]+|$)/i){ |s,c,v| if !(ignore.include? s) && (c != '1') && c =~ /^[^0]+/ ret = ret.gsub(s.to_s,(format("%.3f",c.to_f/rem).to_f).to_s + 'rem') else # debugger ret = ret.gsub(/^0+/,'0').gsub(/(^0+)(\d+)/,'\2') # ret = ret.to_s.gsub(/(^0+)(\d+)/,'\2') end } end return ret end
pxttodpr(str,dpr,multiple)
click to toggle source
# File lib/sassmagic/reset.rb, line 545 def pxttodpr(str,dpr,multiple) ret = str #字符串替换px #'0 1px.jpg 1px 2px;3pxabc 4px'.scan(/([\.\d]+)+(px)+([;\s]+|$)/) #=> [["1", "px", " "], ["2", "px", ";"], ["4", "px", ""]] str.scan(/([\.\d]+)+(px)+([;\s]+|$)/i){ |c,v| if c != '1' ret = ret.gsub(c.to_s,((c.to_i/dpr.to_i) * multiple.to_i).to_s) end } return ret end
rstrip!()
click to toggle source
Strip all trailing whitespace from the output string.
# File lib/sassmagic/reset.rb, line 286 def rstrip! erase! @result.length - 1 - (@result.rindex(/[^\s]/) || -1) end
visit_charset(node)
click to toggle source
# File lib/sassmagic/reset.rb, line 345 def visit_charset(node) for_node(node) {output("@charset \"#{node.name}\";")} end
visit_comment(node)
click to toggle source
# File lib/sassmagic/reset.rb, line 349 def visit_comment(node) return if node.invisible? spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max) content = node.resolved_value.gsub(/^/, spaces) if node.type == :silent content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"} end if (node.style == :compact || node.style == :compressed) && node.type != :loud content.gsub!(/\n +(\* *(?!\/))?/, ' ') end for_node(node) {output(content)} end
visit_cssimport(node)
click to toggle source
# File lib/sassmagic/reset.rb, line 439 def visit_cssimport(node) visit_directive(node) end
visit_directive(node)
click to toggle source
@comment
rubocop:disable MethodLength
# File lib/sassmagic/reset.rb, line 365 def visit_directive(node) was_in_directive = @in_directive tab_str = ' ' * @tabs if !node.has_children || node.children.empty? output(tab_str) for_node(node) {output(node.resolved_value)} output(!node.has_children ? ";" : " {}") return end @in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode) output(tab_str) if node.style != :compressed for_node(node) {output(node.resolved_value)} output(node.style == :compressed ? "{" : " {") output(node.style == :compact ? ' ' : "\n") if node.style != :compressed was_prop = false first = true node.children.each do |child| next if child.invisible? if node.style == :compact if child.is_a?(Sass::Tree::PropNode) with_tabs(first || was_prop ? 0 : @tabs + 1) do visit(child) output(' ') end else if was_prop erase! 1 output "\n" end if first lstrip {with_tabs(@tabs + 1) {visit(child)}} else with_tabs(@tabs + 1) {visit(child)} end rstrip! output "\n" end was_prop = child.is_a?(Sass::Tree::PropNode) first = false elsif node.style == :compressed output(was_prop ? ";" : "") with_tabs(0) {visit(child)} was_prop = child.is_a?(Sass::Tree::PropNode) else with_tabs(@tabs + 1) {visit(child)} output "\n" end end rstrip! if node.style == :expanded output("\n#{tab_str}") elsif node.style != :compressed output(" ") end output("}") ensure @in_directive = was_in_directive end
visit_keyframerule(node)
click to toggle source
@comment
rubocop:enable MethodLength
# File lib/sassmagic/reset.rb, line 674 def visit_keyframerule(node) visit_directive(node) end
visit_media(node)
click to toggle source
@comment
rubocop:enable MethodLength
# File lib/sassmagic/reset.rb, line 430 def visit_media(node) with_tabs(@tabs + node.tabs) {visit_directive(node)} output("\n") if node.style != :compressed && node.group_end end
visit_prop(node)
click to toggle source
属性重写开始
# File lib/sassmagic/reset.rb, line 443 def visit_prop(node) # debugger opt = node.options if $configHash == nil || $configHash == {} #读取配置文件 ctx = Sass::Script::Functions::EvaluationContext.new(Sass::Environment.new(nil, {})) #加载json # 获取设置 filename = opt[:filename] || '' $configHash = ctx.load_json(File.expand_path("#{File.dirname(filename)}/../config/sassmagic.json")) || {} opt = opt.merge $configHash else opt = opt.merge $configHash end multiple = opt["multiple"] || false devicePixelRatio = opt["devicePixelRatio"] || 1 # debugger #干预输出 if multiple # 过滤掉不需要处理的name isNeedChangeMultiple = true; opt["ignore"].each{|v| # debugger if $classignore isNeedChangeMultiple = false end } if isNeedChangeMultiple treated_prop = pxttodpr(node.resolved_value,devicePixelRatio,multiple) else treated_prop = node.resolved_value end else if opt['isNeedPxToRem'] if opt["browserDefaultFontSize"].to_i != 0 configRem = opt["browserDefaultFontSize"].to_i else configRem = 75 end treated_prop = pxtorem(node,node.resolved_value,configRem,opt["ignore"]) else treated_prop = node.resolved_value end end #输出属性resolved_name和值resolved_value return if node.resolved_value.empty? tab_str = ' ' * (@tabs + node.tabs) output(tab_str) for_node(node, :name) {output(node.resolved_name)} if node.style == :compressed output(":") for_node(node, :value) {output(treated_prop)} else output(": ") for_node(node, :value) {output(treated_prop)} output(";") end end
visit_root(node)
click to toggle source
# File lib/sassmagic/reset.rb, line 310 def visit_root(node) #输出样式表 # debugger node.children.each do |child| next if child.invisible? visit(child) unless node.style == :compressed output "\n" if child.is_a?(Sass::Tree::DirectiveNode) && child.has_children && !child.bubbles? output "\n" end end end rstrip! return "" if @result.empty? output "\n" unless Sass::Util.ruby1_8? || @result.ascii_only? if node.style == :compressed # A byte order mark is sufficient to tell browsers that this # file is UTF-8 encoded, and will override any other detection # methods as per http://encoding.spec.whatwg.org/#decode-and-encode. prepend! "\uFEFF" else prepend! "@charset \"UTF-8\";\n" end end @result rescue Sass::SyntaxError => e e.sass_template ||= node.template raise e end
visit_rule(node)
click to toggle source
属性重写结束
@comment rubocop:disable MethodLength
# File lib/sassmagic/reset.rb, line 561 def visit_rule(node) # debugger opt = node.options if $configHash == nil || $configHash == {} #读取配置文件 ctx = Sass::Script::Functions::EvaluationContext.new(Sass::Environment.new(nil, {})) #加载json # 获取设置 filename = opt[:filename] || '' $configHash = ctx.load_json(File.expand_path("#{File.dirname(filename)}/../config/sassmagic.json")) || {} opt = opt.merge $configHash else opt = opt.merge $configHash end # 判断是否不需要rem进行自动处理,通过class $classignore = false opt["ignore"] ||= [] opt["ignore"].each{|v| # debugger if (node.resolved_rules.to_s).include? v $classignore = true end } with_tabs(@tabs + node.tabs) do rule_separator = node.style == :compressed ? ',' : ', ' line_separator = case node.style when :nested, :expanded; "\n" when :compressed; "" else; " " end rule_indent = ' ' * @tabs per_rule_indent, total_indent = if [:nested, :expanded].include?(node.style) [rule_indent, ''] else ['', rule_indent] end joined_rules = node.resolved_rules.members.map do |seq| next if seq.has_placeholder? rule_part = seq.to_s if node.style == :compressed rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ') rule_part.gsub!(/\s*([,+>])\s*/m, '\1') rule_part.strip! end rule_part end.compact.join(rule_separator) joined_rules.lstrip! joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}") old_spaces = ' ' * @tabs if node.style != :compressed if node.options[:debug_info] && !@in_directive visit(debug_info_rule(node.debug_info, node.options)) output "\n" elsif node.options[:trace_selectors] output("#{old_spaces}/* ") output(node.stack_trace.gsub("\n", "\n #{old_spaces}")) output(" */\n") elsif node.options[:line_comments] output("#{old_spaces}/* line #{node.line}") if node.filename relative_filename = if node.options[:css_filename] begin Sass::Util.relative_path_from( node.filename, File.dirname(node.options[:css_filename])).to_s rescue ArgumentError nil end end relative_filename ||= node.filename output(", #{relative_filename}") end output(" */\n") end end end_props, trailer, tabs = '', '', 0 if node.style == :compact separator, end_props, bracket = ' ', ' ', ' { ' trailer = "\n" if node.group_end elsif node.style == :compressed separator, bracket = ';', '{' else tabs = @tabs + 1 separator, bracket = "\n", " {\n" trailer = "\n" if node.group_end end_props = (node.style == :expanded ? "\n" + old_spaces : ' ') end output(total_indent + per_rule_indent) for_node(node, :selector) {output(joined_rules)} output(bracket) with_tabs(tabs) do node.children.each_with_index do |child, i| output(separator) if i > 0 visit(child) end end output(end_props) output("}" + trailer) end end
visit_supports(node)
click to toggle source
# File lib/sassmagic/reset.rb, line 435 def visit_supports(node) visit_media(node) end
with_tabs(tabs) { || ... }
click to toggle source
# File lib/sassmagic/reset.rb, line 226 def with_tabs(tabs) old_tabs, @tabs = @tabs, tabs yield ensure @tabs = old_tabs end
Private Instance Methods
debug_info_rule(debug_info, options)
click to toggle source
# File lib/sassmagic/reset.rb, line 680 def debug_info_rule(debug_info, options) node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info") Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v| rule = Sass::Tree::RuleNode.new([""]) rule.resolved_rules = Sass::Selector::CommaSequence.new( [Sass::Selector::Sequence.new( [Sass::Selector::SimpleSequence.new( [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)], false) ]) ]) prop = Sass::Tree::PropNode.new([""], Sass::Script::Value::String.new(''), :new) prop.resolved_name = "font-family" prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s) rule << prop node << rule end node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed) node end