From 348f94f6317097d988e123d61de4956fa57fc8d7 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sat, 26 Nov 2016 10:15:00 +0100 Subject: [PATCH] Run less filter inside same fiber --- lib/nanoc/base/compilation/compiler.rb | 7 +- lib/nanoc/base/services/filter.rb | 5 ++ lib/nanoc/filters/less.rb | 6 +- spec/nanoc/filters/less_spec.rb | 111 ++++++++++++++++++++----- test/filters/test_less.rb | 71 ---------------- 5 files changed, 105 insertions(+), 95 deletions(-) delete mode 100644 test/filters/test_less.rb diff --git a/lib/nanoc/base/compilation/compiler.rb b/lib/nanoc/base/compilation/compiler.rb index e37ec4668f..68a429b7be 100644 --- a/lib/nanoc/base/compilation/compiler.rb +++ b/lib/nanoc/base/compilation/compiler.rb @@ -233,9 +233,14 @@ def compile_rep(rep) Nanoc::Int::NotificationCenter.post(:compilation_started, rep) res = fiber.resume - if res.is_a?(Nanoc::Int::Errors::UnmetDependency) + case res + when Nanoc::Int::Errors::UnmetDependency Nanoc::Int::NotificationCenter.post(:compilation_suspended, rep, res) raise(res) + when Proc + fiber.resume(res.call) + else + # TODO: raise end end diff --git a/lib/nanoc/base/services/filter.rb b/lib/nanoc/base/services/filter.rb index 72a9945a0d..400f529267 100644 --- a/lib/nanoc/base/services/filter.rb +++ b/lib/nanoc/base/services/filter.rb @@ -184,6 +184,11 @@ def filename end end + # @api private + def on_main_fiber(&block) + Fiber.yield(block) + end + # Creates a dependency from the item that is currently being filtered onto # the given collection of items. In other words, require the given items # to be compiled first before this items is processed. diff --git a/lib/nanoc/filters/less.rb b/lib/nanoc/filters/less.rb index b7bd87473d..c2c79d2cfa 100644 --- a/lib/nanoc/filters/less.rb +++ b/lib/nanoc/filters/less.rb @@ -43,8 +43,10 @@ def run(content, params = {}) # Add filename to load path paths = [File.dirname(@item[:content_filename])] - parser = ::Less::Parser.new(paths: paths) - parser.parse(content).to_css params + on_main_fiber do + parser = ::Less::Parser.new(paths: paths) + parser.parse(content).to_css(params) + end end end end diff --git a/spec/nanoc/filters/less_spec.rb b/spec/nanoc/filters/less_spec.rb index b96308d686..ce51d2ccf5 100644 --- a/spec/nanoc/filters/less_spec.rb +++ b/spec/nanoc/filters/less_spec.rb @@ -1,37 +1,106 @@ describe Nanoc::Filters::Less, site: true, stdio: true do - before do - File.write('content/a.less', '@import "b.less";') - File.write('content/b.less', 'p { color: red; }') + # These tests are high-level in order to interact well with the compiler. This is important for + # this :less filter, because of the way it handles fibers. + before do File.open('Rules', 'w') do |io| - io.write "compile '/a.less' do\n" - io.write " filter :less\n" - io.write " write '/a.css'\n" - io.write "end\n" - io.write "\n" - io.write "compile '/b.less' do\n" + io.write "compile '/**/*.less' do\n" io.write " filter :less\n" + io.write " write item.identifier.without_ext + '.css'\n" io.write "end\n" end end - it 'compiles a.less' do - skip 'flaky test' + context 'one file' do + let(:content_a) { 'p { color: red; }' } + + before do + File.write('content/a.less', content_a) + end + + it 'compiles a.less' do + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*red;?\s*\}/) + end + + context 'with compression' do + let(:content_a) { '.foo { bar: a; } .bar { foo: b; }' } + + before do + File.open('Rules', 'w') do |io| + io.write "compile '/*.less' do\n" + io.write " filter :less, compress: true\n" + io.write " write item.identifier.without_ext + '.css'\n" + io.write "end\n" + end + end + + it 'compiles and compresses a.less' do + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/a.css')).to match(/^\.foo\{bar:a\}\n?\.bar\{foo:b\}/) + end + end + end + + context 'two files' do + let(:content_a) { '@import "b.less";' } + let(:content_b) { 'p { color: red; }' } + + before do + File.write('content/a.less', content_a) + File.write('content/b.less', content_b) + end + + it 'compiles a.less' do + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*red;?\s*\}/) + end + + it 'recompiles a.less if b.less has changed' do + Nanoc::CLI.run(%w(compile)) + + File.write('content/b.less', 'p { color: blue; }') + + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*blue;?\s*\}/) + end + end + + context 'paths relative to site directory' do + let(:content_a) { '@import "content/foo/bar/imported_file.less";' } + let(:content_b) { 'p { color: red; }' } - Nanoc::CLI.run(%w(compile)) - expect(Dir['output/*']).to eql(['output/a.css']) - expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*red;?\s*\}/) + before do + FileUtils.mkdir_p('content/foo/bar') + + File.write('content/a.less', content_a) + File.write('content/foo/bar/imported_file.less', content_b) + end + + it 'compiles a.less' do + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*red;?\s*\}/) + end + + it 'recompiles a.less if b.less has changed' end - it 'recompiles a.less if b.less has changed' do - skip 'flaky test' + context 'paths relative to current file' do + let(:content_a) { '@import "bar/imported_file.less";' } + let(:content_b) { 'p { color: red; }' } + + before do + FileUtils.mkdir_p('content/foo/bar') - Nanoc::CLI.run(%w(compile)) + File.write('content/foo/a.less', content_a) + File.write('content/foo/bar/imported_file.less', content_b) + end - File.write('content/b.less', 'p { color: blue; }') + it 'compiles a.less' do + Nanoc::CLI.run(%w(compile)) + expect(File.read('output/foo/a.css')).to match(/^p\s*\{\s*color:\s*red;?\s*\}/) + end - Nanoc::CLI.run(%w(compile)) - expect(Dir['output/*']).to eql(['output/a.css']) - expect(File.read('output/a.css')).to match(/^p\s*\{\s*color:\s*blue;?\s*\}/) + it 'recompiles a.less if b.less has changed' end end diff --git a/test/filters/test_less.rb b/test/filters/test_less.rb deleted file mode 100644 index a8255fcb99..0000000000 --- a/test/filters/test_less.rb +++ /dev/null @@ -1,71 +0,0 @@ -class Nanoc::Filters::LessTest < Nanoc::TestCase - def view_context - dependency_tracker = Nanoc::Int::DependencyTracker.new(nil) - Nanoc::ViewContext.new(reps: nil, items: nil, dependency_tracker: dependency_tracker, compiler: nil) - end - - def test_filter - if_have 'less' do - # Create item - @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), view_context) - - # Create filter - filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item]) - - # Run filter - result = filter.setup_and_run('.foo { bar: 1 + 1 }') - assert_match(/\.foo\s*\{\s*bar:\s*2;?\s*\}/, result) - end - end - - def test_filter_with_paths_relative_to_site_directory - if_have 'less' do - # Create file to import - FileUtils.mkdir_p('content/foo/bar') - File.open('content/foo/bar/imported_file.less', 'w') { |io| io.write('p { color: red; }') } - - # Create item - @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), view_context) - - # Create filter - filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item]) - - # Run filter - result = filter.setup_and_run('@import "content/foo/bar/imported_file.less";') - assert_match(/p\s*\{\s*color:\s*red;?\s*\}/, result) - end - end - - def test_filter_with_paths_relative_to_current_file - if_have 'less' do - # Create file to import - FileUtils.mkdir_p('content/foo/bar') - File.open('content/foo/bar/imported_file.less', 'w') { |io| io.write('p { color: red; }') } - - # Create item - File.open('content/foo/bar.txt', 'w') { |io| io.write('meh') } - @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), view_context) - - # Create filter - filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item]) - - # Run filter - result = filter.setup_and_run('@import "bar/imported_file.less";') - assert_match(/p\s*\{\s*color:\s*red;?\s*\}/, result) - end - end - - def test_compression - if_have 'less' do - # Create item - @item = Nanoc::ItemWithRepsView.new(Nanoc::Int::Item.new('blah', { content_filename: 'content/foo/bar.txt' }, '/foo/bar/'), view_context) - - # Create filter - filter = ::Nanoc::Filters::Less.new(item: @item, items: [@item]) - - # Run filter with compress option - result = filter.setup_and_run('.foo { bar: a; } .bar { foo: b; }', compress: true) - assert_match(/^\.foo\{bar:a\}\n?\.bar\{foo:b\}/, result) - end - end -end