Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap-rubygem.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tasks/converter/less_conversion.rb')
-rw-r--r--tasks/converter/less_conversion.rb88
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))