From a2d61f976ff9a796d7bd1f4442b4f31f314c5a0a Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Wed, 3 Oct 2018 20:59:45 +0530 Subject: [PATCH] Optimize rendering Liquid templates (#7136) Merge pull request 7136 --- docs/_docs/upgrading/3-to-4.md | 27 +++++++++++++++++++++++++-- lib/jekyll/liquid_renderer.rb | 11 ++++++++++- lib/jekyll/liquid_renderer/file.rb | 4 +++- lib/jekyll/tags/include.rb | 8 +------- lib/jekyll/tags/link.rb | 4 +--- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/docs/_docs/upgrading/3-to-4.md b/docs/_docs/upgrading/3-to-4.md index 8bd1fde1..9ccb776d 100644 --- a/docs/_docs/upgrading/3-to-4.md +++ b/docs/_docs/upgrading/3-to-4.md @@ -19,9 +19,32 @@ If you're using Ruby >= 2.3.0, go ahead and fetch the latest version of Jekyll: gem update jekyll ``` ---- - *Insert sections here* +### Template rendering + +We've slightly altered the way Jekyll parses and renders your various templates to improve +the overall build times. Jekyll now parses a template once, caches it internally and then +renders the parsed template multiple times as required by your pages and documents. + +The downside to this is that some of the community-authored plugins may not work as they +previously used to. + +#### For Plugin-authors + +* If your plugin depends on the following code: `site.liquid_renderer.file(path).parse(content)`, +note that the return value (`template`, an instance of *`Liquid::Template`*), from that line will +always be the **same object** for a given `path`.
+The *`template`* instance is then rendered as previously, with respect to the `payload` passed to it. +You'll therefore have to ensure that *`payload`* is not memoized or cached in your plugin instance. + +* If its a requirement that `template` you get from the above step *be different* at all times, +you can invoke *`Liquid::Template`* directly: + + + ```diff + - template = site.liquid_renderer.file(path).parse(content) + + template = Liquid::Template.parse(content) + ``` --- diff --git a/lib/jekyll/liquid_renderer.rb b/lib/jekyll/liquid_renderer.rb index 55e4e0fb..f895a800 100644 --- a/lib/jekyll/liquid_renderer.rb +++ b/lib/jekyll/liquid_renderer.rb @@ -18,6 +18,7 @@ module Jekyll def reset @stats = {} + @cache = {} end def file(filename) @@ -50,10 +51,18 @@ module Jekyll "#{error.message} in #{path}" end + # A persistent cache to store and retrieve parsed templates based on the filename + # via `LiquidRenderer::File#parse` + # + # It is emptied when `self.reset` is called. + def cache + @cache ||= {} + end + private def filename_regex - @filename_regex ||= %r!\A(#{source_dir}/|#{theme_dir}/|\W*)(.*)!i + @filename_regex ||= %r!\A(#{source_dir}/|#{theme_dir}/|/*)(.*)!i end def new_profile_hash diff --git a/lib/jekyll/liquid_renderer/file.rb b/lib/jekyll/liquid_renderer/file.rb index 574bbd31..8d38ffbf 100644 --- a/lib/jekyll/liquid_renderer/file.rb +++ b/lib/jekyll/liquid_renderer/file.rb @@ -10,8 +10,9 @@ module Jekyll def parse(content) measure_time do - @template = Liquid::Template.parse(content, :line_numbers => true) + @renderer.cache[@filename] ||= Liquid::Template.parse(content, :line_numbers => true) end + @template = @renderer.cache[@filename] self end @@ -24,6 +25,7 @@ module Jekyll end end + # This method simply 'rethrows any error' before attempting to render the template. def render!(*args) measure_time do measure_bytes do diff --git a/lib/jekyll/tags/include.rb b/lib/jekyll/tags/include.rb index 1f47324b..4d09e58e 100644 --- a/lib/jekyll/tags/include.rb +++ b/lib/jekyll/tags/include.rb @@ -90,13 +90,7 @@ module Jekyll # Render the variable if required def render_variable(context) - if @file =~ VARIABLE_SYNTAX - partial = context.registers[:site] - .liquid_renderer - .file("(variable)") - .parse(@file) - partial.render!(context) - end + Liquid::Template.parse(@file).render(context) if @file =~ VARIABLE_SYNTAX end def tag_includes_dirs(context) diff --git a/lib/jekyll/tags/link.rb b/lib/jekyll/tags/link.rb index 124830e2..b8496a0a 100644 --- a/lib/jekyll/tags/link.rb +++ b/lib/jekyll/tags/link.rb @@ -17,9 +17,7 @@ module Jekyll def render(context) site = context.registers[:site] - - liquid = site.liquid_renderer.file("(jekyll:link)") - relative_path = liquid.parse(@relative_path).render(context) + relative_path = Liquid::Template.parse(@relative_path).render(context) site.each_site_file do |item| return item.url if item.relative_path == relative_path