| # encoding: utf-8 |
| |
| require 'sass' |
| require 'jekyll/utils' |
| |
| module Jekyll |
| module Converters |
| class Scss < Converter |
| BYTE_ORDER_MARK = /^\xEF\xBB\xBF/ |
| SyntaxError = Class.new(ArgumentError) |
| |
| safe true |
| priority :low |
| |
| ALLOWED_STYLES = %w(nested expanded compact compressed).freeze |
| |
| def matches(ext) |
| ext =~ /^\.scss$/i |
| end |
| |
| def output_ext(ext) |
| ".css" |
| end |
| |
| def safe? |
| !!@config["safe"] |
| end |
| |
| def jekyll_sass_configuration |
| options = @config["sass"] || {} |
| unless options["style"].nil? |
| options["style"] = options["style"].to_s.gsub(/\A:/, '').to_sym |
| end |
| options |
| end |
| |
| def sass_build_configuration_options(overrides) |
| if safe? |
| { |
| :load_paths => sass_load_paths, |
| :syntax => syntax, |
| :style => sass_style, |
| :cache => false |
| } |
| else |
| Jekyll::Utils.symbolize_hash_keys( |
| Jekyll::Utils.deep_merge_hashes( |
| jekyll_sass_configuration, |
| overrides |
| ) |
| ) |
| end |
| end |
| |
| def syntax |
| :scss |
| end |
| |
| def sass_dir |
| return "_sass" if jekyll_sass_configuration["sass_dir"].to_s.empty? |
| jekyll_sass_configuration["sass_dir"] |
| end |
| |
| def sass_style |
| style = jekyll_sass_configuration.fetch("style", :compact) |
| ALLOWED_STYLES.include?(style.to_s) ? style.to_sym : :compact |
| end |
| |
| def user_sass_load_paths |
| Array(jekyll_sass_configuration["load_paths"]) |
| end |
| |
| def sass_dir_relative_to_site_source |
| Jekyll.sanitized_path(site_source, sass_dir) |
| end |
| |
| def sass_load_paths |
| paths = user_sass_load_paths + [sass_dir_relative_to_site_source] |
| |
| if safe? |
| # Sanitize paths to prevent any attack vectors (.e.g. `/**/*`) |
| paths.map! { |path| Jekyll.sanitized_path(site_source, path) } |
| end |
| |
| # Expand file globs (e.g. `node_modules/*/node_modules` ) |
| Dir.chdir(site_source) do |
| paths = paths.map { |path| Dir.glob(path) }.flatten.uniq |
| |
| paths.map! do |path| |
| if safe? |
| # Sanitize again in case globbing was able to do something crazy. |
| Jekyll.sanitized_path(site_source, path) |
| else |
| File.expand_path(path) |
| end |
| end |
| end |
| |
| paths.select { |path| File.directory?(path) } |
| end |
| |
| def allow_caching? |
| !safe? |
| end |
| |
| def add_charset? |
| !!jekyll_sass_configuration["add_charset"] |
| end |
| |
| def sass_configs |
| sass_build_configuration_options({ |
| "syntax" => syntax, |
| "cache" => allow_caching?, |
| "load_paths" => sass_load_paths |
| }) |
| end |
| |
| def convert(content) |
| output = ::Sass.compile(content, sass_configs) |
| replacement = add_charset? ? '@charset "UTF-8";' : '' |
| output.sub(BYTE_ORDER_MARK, replacement) |
| rescue ::Sass::SyntaxError => e |
| raise SyntaxError.new("#{e.to_s} on line #{e.sass_line}") |
| end |
| |
| private |
| def site_source |
| @site_source ||= File.expand_path(@config["source"]).freeze |
| end |
| end |
| end |
| end |