Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sass improvements #1365

Merged
merged 2 commits into from Oct 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 13 additions & 3 deletions common/spec/spec_helper_foot.rb
Expand Up @@ -384,19 +384,29 @@ def sort(coll)

b = dependency_store.objects_causing_outdatedness_of(@from)

(b - a).any?
@actual = b - a

if @onto
values_match?(@onto, @actual)
else
@actual.any?
end
end

chain :onto do |onto|
@onto = onto
end

description do
"create a dependency from #{expected.inspect}"
end

failure_message do |_actual|
"expected a dependency to be created from #{expected.inspect}"
"expected a dependency to be created from #{expected.inspect}#{@onto ? " onto #{@onto.inspect}" : nil}, but generated #{@actual.inspect}"
end

failure_message_when_negated do |_actual|
"expected no dependency to be created from #{expected.inspect}"
"expected no dependency to be created from #{expected.inspect}, but generated #{@actual.inspect}"
end
end

Expand Down
2 changes: 1 addition & 1 deletion nanoc/lib/nanoc/base/services/filter.rb
Expand Up @@ -34,7 +34,7 @@ class Filter < Nanoc::Int::Context
class << self
def define(ident, &block)
filter_class = Class.new(::Nanoc::Filter) { identifier(ident) }
filter_class.send(:define_method, :run) do |content, params|
filter_class.send(:define_method, :run) do |content, params = {}|
instance_exec(content, params, &block)
end
end
Expand Down
168 changes: 138 additions & 30 deletions nanoc/lib/nanoc/filters/sass.rb
@@ -1,45 +1,153 @@
# frozen_string_literal: true

module Nanoc::Filters
# @api private
class Sass < Nanoc::Filter
identifier :sass

requires 'sass', 'nanoc/filters/sass/sass_filesystem_importer'

# Runs the content through [Sass](http://sass-lang.com/).
# Parameters passed to this filter will be passed on to Sass.
#
# @param [String] content The content to filter
#
# @return [String] The filtered content
def run(content, params = {})
Nanoc::Filter.define(:sass) do |content, params = {}|
include Sass
css(self, @item_rep, content, params)
end

Nanoc::Filter.define(:sass_sourcemap) do |content, params = {}|
include Sass
sourcemap(self, @item_rep, content, params)
end

require 'sass'

module Sass
def css(filter, rep, content, params)
css, = render(filter, rep, content, params)
css
end

def sourcemap(filter, rep, content, params)
_, sourcemap = render(filter, rep, content, params)
sourcemap
end

private

def render(filter, rep, content, params = {})
importer = NanocSassImporter.new(filter)

options = params.merge(
nanoc_current_filter: self,
filename: @item&.raw_filename,
load_paths: [importer, *params[:load_paths]&.reject { |p| p.is_a?(String) && %r{^content/} =~ p }],
importer: importer,
filename: rep.item.identifier.to_s,
cache: false,
)
sourcemap_path = options.delete(:sourcemap_path)

engine = ::Sass::Engine.new(content, options)
engine.render
css, sourcemap = sourcemap_path ? engine.render_with_sourcemap(sourcemap_path) : engine.render
[css, sourcemap&.to_json(css_uri: rep.path, type: rep.path.nil? ? :inline : :auto)]
end

def self.item_filename_map_for_config(config, items)
@item_filename_map ||= {}
@item_filename_map[config] ||=
{}.tap do |map|
items.each do |item|
if item.raw_filename
path = Pathname.new(item.raw_filename).realpath.to_s
map[path] = item
# @api private
class NanocSassImporter < ::Sass::Importers::Filesystem
attr_reader :filter

def initialize(filter)
@filter = filter
super('.')
end

def find_relative(name, base_identifier, options)
base_raw_filename = filter.items[base_identifier].raw_filename

# we can't resolve a relative filename from an in-memory item
return unless base_raw_filename

raw_filename, syntax = ::Sass::Util.destructure(find_real_file(File.dirname(base_raw_filename), name, options))
return unless raw_filename

item = raw_filename_to_item(raw_filename)
# it doesn't make sense to import a file, from Nanoc's content if the corresponding item has been deleted
raise "unable to map #{raw_filename} to any item" if item.nil?

filter.depend_on([item])

options[:syntax] = syntax
options[:filename] = item.identifier.to_s
options[:importer] = self
::Sass::Engine.new(item.raw_content, options)
end

def find(identifier, options)
items = filter.items.find_all(identifier)
return if items.empty?

content = if items.size == 1
items.first.compiled_content
else
items.map { |item| %(@import "#{item.identifier}";) }.join("\n")
end

options[:syntax] = :scss
options[:filename] = identifier.to_s
options[:importer] = self
::Sass::Engine.new(content, options)
end

def key(identifier, _options)
[self.class.name + ':' + root, identifier.to_s]
end

def public_url(identifier, _sourcemap_directory)
path = filter.items[identifier].path
return path unless path.nil?

raw_filename = filter.items[identifier].raw_filename
return if raw_filename.nil?

::Sass::Util.file_uri_from_path(raw_filename)
end

def to_s
'Nanoc Sass Importer'
end

def self.raw_filename_to_item_map_for_config(config, items)
@raw_filename_to_item_map ||= {}
@raw_filename_to_item_map[config.object_id] ||=
{}.tap do |map|
items.each do |item|
if item.raw_filename
path = Pathname.new(item.raw_filename).realpath.to_s
map[path] = item
end
end
end
end
end
end

def raw_filename_to_item(filename)
realpath = Pathname.new(filename).realpath.to_s

def imported_filename_to_item(filename)
realpath = Pathname.new(filename).realpath.to_s
map = self.class.raw_filename_to_item_map_for_config(filter.config, filter.items)
map[realpath]
end
end
end

map = self.class.item_filename_map_for_config(@config, @items)
map[realpath]
module ::Sass::Script::Functions
def nanoc(string, params)
assert_type string, :String
assert_type params, :Hash
result = options[:importer].filter.instance_eval(string.value)
case result
when TrueClass, FalseClass
bool(result)
when Array
list(result, :comma)
when Hash
map(result)
when nil
null
when Numeric
number(result)
else
params['unquote'] ? unquoted_string(result) : quoted_string(result)
end
end
declare :nanoc, [:string], var_kwargs: true
end
end
22 changes: 0 additions & 22 deletions nanoc/lib/nanoc/filters/sass/sass_filesystem_importer.rb

This file was deleted.

1 change: 0 additions & 1 deletion nanoc/nanoc.manifest
Expand Up @@ -227,7 +227,6 @@ lib/nanoc/filters/redcloth.rb
lib/nanoc/filters/relativize_paths.rb
lib/nanoc/filters/rubypants.rb
lib/nanoc/filters/sass.rb
lib/nanoc/filters/sass/sass_filesystem_importer.rb
lib/nanoc/filters/slim.rb
lib/nanoc/filters/typogruby.rb
lib/nanoc/filters/uglify_js.rb
Expand Down