require 'sass' module Middleman module Renderers # Sass renderer class Sass < ::Middleman::Extension opts = { output_style: :nested } opts[:line_comments] = false if ENV['TEST'] define_setting :sass, opts, 'Sass engine options' define_setting :sass_assets_paths, [], 'Paths to extra SASS/SCSS files' define_setting :sass_source_maps, nil, 'Whether to inline sourcemap into Sass' # Setup extension def initialize(app, options={}, &block) super logger.info '== Preferring use of LibSass' if defined?(::SassC) app.files.ignore :sass_cache, :source, /(^|\/)\.sass-cache\// # Tell Tilt to use it as well (for inline sass blocks) ::Tilt.register 'sass', SassPlusCSSFilenameTemplate ::Tilt.prefer(SassPlusCSSFilenameTemplate) # Tell Tilt to use it as well (for inline scss blocks) ::Tilt.register 'scss', ScssPlusCSSFilenameTemplate ::Tilt.prefer(ScssPlusCSSFilenameTemplate) require 'middleman-core/renderers/sass_functions' end # A SassTemplate for Tilt which outputs debug messages class SassPlusCSSFilenameTemplate < ::Tilt::SassTemplate def initialize(*args, &block) super @context = @options[:context] if @options.key?(:context) end # Define the expected syntax for the template # @return [Symbol] def syntax :sass end def prepare; end # Add exception messaging # @param [Class] context # @return [String] def evaluate(context, _) @context ||= context sass_module = if defined?(::SassC) ::SassC else ::Sass end @engine = sass_module::Engine.new(data, sass_options) begin @engine.render rescue sass_module::SyntaxError => e ::Sass::SyntaxError.exception_to_css(e) end end # Change Sass path, for url functions, to the build folder if we're building # @return [Hash] def sass_options ctx = @context more_opts = { load_paths: ::Sass.load_paths | ctx.app.config[:sass_assets_paths], filename: eval_file, line: line, syntax: syntax, custom: (options[:custom] || {}).merge( middleman_context: ctx.app, current_resource: ctx.current_resource ) } if ctx.app.config[:sass_source_maps] || (ctx.app.config[:sass_source_maps].nil? && ctx.app.development?) more_opts[:source_map_file] = '.' more_opts[:source_map_embed] = true more_opts[:source_map_contents] = true end if ctx.is_a?(::Middleman::TemplateContext) && file more_opts[:css_filename] = file.sub(/\.s[ac]ss$/, '') end options.merge(more_opts) end end # SCSS version of the above template class ScssPlusCSSFilenameTemplate < SassPlusCSSFilenameTemplate # Define the expected syntax for the template # @return [Symbol] def syntax :scss end end end end end