discourse/lib/sass/discourse_sass_importer.rb

139 lines
4.1 KiB
Ruby

# This custom importer is used for site customizations. This is similar to the
# Sprockets::SassImporter implementation provided in sass-rails since that is used
# during asset precompilation.
class DiscourseSassImporter < Sass::Importers::Filesystem
GLOB = /\*|\[.+\]/
# Depending upon where this is passed we might either be passed a string as the
# first argument or a sprockets context. If the first argument is a sprockets
# context we store it and use it to mark dependencies.
def initialize(*args)
@context = args.first unless args.first.is_a? String
@root = Rails.root.join('app', 'assets', 'stylesheets').to_s
@same_name_warnings = Set.new
end
def extensions
{
'css' => :scss,
'css.scss' => :scss,
'css.sass' => :sass,
'css.erb' => :scss,
'scss.erb' => :scss,
'sass.erb' => :sass,
'css.scss.erb' => :scss,
'css.sass.erb' => :sass
}.merge!(super)
end
def special_imports
{
"plugins" => DiscoursePluginRegistry.stylesheets,
"plugins_mobile" => DiscoursePluginRegistry.mobile_stylesheets,
"plugins_desktop" => DiscoursePluginRegistry.desktop_stylesheets,
"plugins_variables" => DiscoursePluginRegistry.sass_variables,
"theme_variables" => [ColorScheme::BASE_COLORS_FILE]
}
end
def find_relative(name, base, options)
if name =~ GLOB
glob_imports(name, Pathname.new(base), options)
else
engine_from_path(name, File.dirname(base), options)
end
end
def find(name, options)
if special_imports.has_key? name
if name == "theme_variables"
contents = ""
if color_scheme = ColorScheme.enabled
ColorScheme.base_colors.each do |name, base_hex|
override = color_scheme.colors_by_name[name]
contents << "$#{name}: ##{override ? override.hex : base_hex} !default;\n"
end
else
special_imports[name].each do |css_file|
contents << File.read(css_file)
end
end
Sass::Engine.new(contents, options.merge(
filename: "#{name}.scss",
importer: self,
syntax: :scss
))
else
stylesheets = special_imports[name]
contents = ""
stylesheets.each do |css_file|
if css_file =~ /\.scss$/
contents << "@import '#{css_file}';"
else
contents << File.read(css_file)
end
depend_on(css_file)
end
Sass::Engine.new(contents, options.merge(
filename: "#{name}.scss",
importer: self,
syntax: :scss
))
end
elsif name =~ GLOB
nil # globs must be relative
else
engine_from_path(name, root, options)
end
end
def each_globbed_file(glob, base_pathname, options)
Dir["#{base_pathname}/#{glob}"].sort.each do |filename|
next if filename == options[:filename]
yield filename # assume all matching files are requirable
end
end
def glob_imports(glob, base_pathname, options)
contents = ""
each_globbed_file(glob, base_pathname.dirname, options) do |filename|
depend_on(filename)
unless File.directory?(filename)
contents << "@import #{Pathname.new(filename).relative_path_from(base_pathname.dirname).to_s.inspect};\n"
end
end
return nil if contents.empty?
Sass::Engine.new(contents, options.merge(
filename: base_pathname.to_s,
importer: self,
syntax: :scss
))
end
private
def depend_on(filename)
if @context
@context.depend_on(filename)
@context.depend_on(globbed_file_parent(filename))
end
end
def globbed_file_parent(filename)
if File.directory?(filename)
File.expand_path('..', filename)
else
File.dirname(filename)
end
end
def engine_from_path(name, dir, options)
full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
return unless full_filename && File.readable?(full_filename)
depend_on(full_filename)
Sass::Engine.for_file(full_filename, options)
end
end