diff options
Diffstat (limited to 'tasks/converter/less_conversion.rb')
-rw-r--r-- | tasks/converter/less_conversion.rb | 88 |
1 files changed, 60 insertions, 28 deletions
diff --git a/tasks/converter/less_conversion.rb b/tasks/converter/less_conversion.rb index a903aec..0d46ce9 100644 --- a/tasks/converter/less_conversion.rb +++ b/tasks/converter/less_conversion.rb @@ -22,7 +22,7 @@ class Converter # match any brace that opens or closes a properties body BRACE_RE = /#{RULE_OPEN_BRACE_RE}|#{RULE_CLOSE_BRACE_RE}/m BRACE_RE_REVERSE = /#{RULE_OPEN_BRACE_RE_REVERSE}|#{RULE_CLOSE_BRACE_RE_REVERSE}/m - # valid + # valid characters in mixin definitions SCSS_MIXIN_DEF_ARGS_RE = /[\w\-,\s$:#%()]*/ LESS_MIXIN_DEF_ARGS_RE = /[\w\-,;.\s@:#%()]*/ @@ -31,27 +31,34 @@ class Converter # These mixins will get vararg definitions in SCSS (not supported by LESS): VARARG_MIXINS = %w( - scale transition transition-duration transition-property transition-transform box-shadow - ) + scale transition transition-duration transition-property transition-transform box-shadow + ) + + # A list of classes that will be extracted into mixins + # Only the top-level selectors of form .CLASS { ... } are extracted. CLASS must not be used in any other rule definition. + # This is a work-around for libsass @extend issues + CLASSES_TO_MIXINS = %w( + list-unstyled form-inline + ) # Convert a snippet of bootstrap LESS to Scss def convert_less(less) - load_shared less = convert_to_scss(less) less = yield(less) if block_given? less end - def load_shared + def shared_mixins @shared_mixins ||= begin log_status ' Reading shared mixins from mixins.less' - read_mixins read_files('less', bootstrap_less_files.grep(/mixins\//)).values.join("\n"), nested: NESTED_MIXINS + CLASSES_TO_MIXINS + read_mixins(read_files('less', bootstrap_less_files.grep(/mixins\//)).values.join("\n"), + nested: NESTED_MIXINS) end end def process_stylesheet_assets log_status 'Processing stylesheets...' - files = read_files('less', bootstrap_less_files) + files = read_files('less', bootstrap_less_files) save_to = @save_to[:scss] log_status ' Converting LESS files to Scss:' @@ -94,7 +101,7 @@ class Converter // NB: in Sass 3.3 there is a native function: function-exists(twbs-font-path) $bootstrap-sass-asset-helper: #{sass_fn_exists('twbs-font-path')} !default; SCSS - file = replace_all file, %r{(\$icon-font-path): \s*"(.*)" (!default);}, "\n" + unindent(<<-SCSS, 14) + file = replace_all file, %r{(\$icon-font-path): \s*"(.*)" (!default);}, "\n" + unindent(<<-SCSS, 14) // [converter] Asset helpers such as Sprockets and Node.js Mincer do not resolve relative paths \\1: if($bootstrap-sass-asset-helper, "bootstrap/", "\\2bootstrap/") \\3; SCSS @@ -114,7 +121,6 @@ class Converter when 'thumbnails.less', 'labels.less', 'badges.less' file = extract_nested_rule file, 'a&' when 'glyphicons.less' - file = bootstrap_font_files.map { |p| %Q(//= depend_on "bootstrap/#{File.basename(p)}") } * "\n" + "\n" + file file = replace_rules(file, '@font-face') { |rule| rule = replace_all rule, /(\$icon-font(?:-\w+)+)/, '#{\1}' replace_asset_url rule, :font @@ -136,6 +142,11 @@ class Converter main_to = File.expand_path("#{save_to}/../_bootstrap.scss") save_file main_to, File.read(main_from).gsub(/ "/, ' "bootstrap/') File.delete(main_from) + + # generate variables template + save_file 'templates/project/_bootstrap-variables.sass', + "// Override Bootstrap variables here (defaults from bootstrap-sass v<%= Bootstrap::VERSION %>):\n\n" + + File.read("#{save_to}/_variables.scss").gsub(/^(?=\$)/, '// ').gsub(/ !default;/, '') end def bootstrap_less_files @@ -147,10 +158,11 @@ class Converter # apply general less to scss conversion def convert_to_scss(file) # get local mixin names before converting the definitions - mixins = @shared_mixins + read_mixins(file) + mixins = shared_mixins + read_mixins(file) file = replace_vars(file) file = replace_mixin_definitions(file) file = replace_mixins(file, mixins) + file = extract_mixins_from_selectors(file, CLASSES_TO_MIXINS.inject({}) { |h, cl| h.update(".#{cl}" => cl) }) file = replace_spin(file) file = replace_fadein(file) file = replace_image_urls(file) @@ -371,7 +383,7 @@ SASS sel = parent_sel + sel[1..-1] end # unwrap, and replace @include - unindent unwrap_rule_block(rule).gsub(/(@include [\w-]+)\(([\$\w\-,\s]*)\)/) { + unindent unwrap_rule_block(rule).gsub(/(@include [\w-]+)\(?([\$\w\-,\s]*)\)?/) { args = $2 "#{cmt}#{$1}('#{sel.gsub(/\s+/, ' ')}'#{', ' if args && !args.empty?}#{args})" } @@ -388,6 +400,25 @@ SASS end end + # .btn { ... } -> @mixin btn { ... }; .btn { @include btn } + def extract_mixins_from_selectors(file, selectors_to_mixins) + selectors_to_mixins.each do |selector, mixin| + file = replace_rules file, Regexp.escape(selector), prefix: false do |selector_css| + log_transform "#{selector} { ... } -> @mixin #{mixin} { ... }; #{selector} { @include #{mixin} } ", from: 'extract_mixins_from_selectors' + <<-SCSS +// [converter] extracted from `#{selector}` for libsass compatibility +@mixin #{mixin} {#{unwrap_rule_block(selector_css)} +} +// [converter] extracted as `@mixin #{mixin}` for libsass compatibility +#{selector} { + @include #{mixin}; +} + SCSS + end + end + file + end + # @include and @extend from LESS: # .mixin() -> @include mixin() # #scope > .mixin() -> @include scope-mixin() @@ -396,14 +427,11 @@ SASS mixin_pattern = /(\s+)(([#|\.][\w-]+\s*>\s*)*)\.([\w-]+\(.*\))(?!\s\{)/ less = less.gsub(mixin_pattern) do |match| - matches = match.scan(mixin_pattern).flatten - scope = matches[1] || '' - if scope != '' - scope = scope.scan(/[\w-]+/).join('-') + '-' - end + matches = match.scan(mixin_pattern).flatten + scope = matches[1] && matches[1] != '' ? matches[1].scan(/[\w-]+/).join('-') + '-' : '' mixin_name = match.scan(/\.([\w-]+)\(.*\)\s?\{?/).first if mixin_name && mixin_names.include?("#{scope}#{mixin_name.first}") - "#{matches.first}@include #{scope}#{matches.last}".gsub(/; \$/, ", $").sub(/;\)$/, ')') + "#{matches.first}@include #{scope}#{matches.last.gsub(/;\s*\$/, ', $').sub(/;\)$/, ')').sub(/\(\)$/, '')}" else "#{matches.first}@extend .#{scope}#{matches.last.gsub(/\(\)/, '')}" end @@ -414,7 +442,7 @@ SASS selector =~ /\.([\w-]+)/ mixin = $1 if mixin && mixin_names.include?(mixin) - "@include #{mixin}()" + "@include #{mixin}" else "@extend #{selector}" end @@ -548,16 +576,20 @@ SASS # option :comments -- include immediately preceding comments in rule_block # # replace_rules(".a{ \n .b{} }", '.b') { |rule, pos| ">#{rule}<" } #=> ".a{ \n >.b{}< }" - def replace_rules(less, rule_prefix = SELECTOR_RE, options = {}, &block) - options = {comments: true}.merge(options || {}) - less = less.dup - s = CharStringScanner.new(less) - rule_re = /(?:#{rule_prefix}[#{SELECTOR_CHAR})=(\s]*?#{RULE_OPEN_BRACE_RE})/ - if options[:comments] - rule_start_re = /(?:#{COMMENT_RE}*)^#{rule_re}/ - else - rule_start_re = /^#{rule_re}/ - end + def replace_rules(less, selector = SELECTOR_RE, options = {}, &block) + options = {prefix: true, comments: true}.merge(options || {}) + less = less.dup + s = CharStringScanner.new(less) + rule_re = if options[:prefix] + /(?:#{selector}[#{SELECTOR_CHAR})=(\s]*?#{RULE_OPEN_BRACE_RE})/ + else + /#{selector}[\s]*#{RULE_OPEN_BRACE_RE}/ + end + rule_start_re = if options[:comments] + /(?:#{COMMENT_RE}*)^#{rule_re}/ + else + /^#{rule_re}/ + end positions = [] while (rule_start = s.scan_next(rule_start_re)) |