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

Initialize include-files as Jekyll objects #8158

Merged
merged 15 commits into from May 25, 2020
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
1 change: 1 addition & 0 deletions lib/jekyll.rb
Expand Up @@ -54,6 +54,7 @@ module Jekyll
autoload :FrontmatterDefaults, "jekyll/frontmatter_defaults"
autoload :Hooks, "jekyll/hooks"
autoload :Layout, "jekyll/layout"
autoload :Inclusion, "jekyll/inclusion"
autoload :Cache, "jekyll/cache"
autoload :CollectionReader, "jekyll/readers/collection_reader"
autoload :DataReader, "jekyll/readers/data_reader"
Expand Down
32 changes: 32 additions & 0 deletions lib/jekyll/inclusion.rb
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Jekyll
class Inclusion
attr_reader :site, :name, :path
private :site

def initialize(site, base, name)
@site = site
@name = name
@path = PathManager.join(base, name)
end

def render(context)
@template ||= site.liquid_renderer.file(path).parse(content)
@template.render!(context)
rescue Liquid::Error => e
e.template_name = path
e.markup_context = "included " if e.markup_context.nil?
raise e
end

def content
@content ||= File.read(path, **site.file_read_opts)
end

def inspect
"#{self.class} #{path.inspect}"
end
alias_method :to_s, :inspect
end
end
5 changes: 4 additions & 1 deletion lib/jekyll/site.rb
Expand Up @@ -3,7 +3,7 @@
module Jekyll
class Site
attr_reader :source, :dest, :cache_dir, :config
attr_accessor :layouts, :pages, :static_files, :drafts,
attr_accessor :layouts, :pages, :static_files, :drafts, :inclusions,
:exclude, :include, :lsi, :highlighter, :permalink_style,
:time, :future, :unpublished, :safe, :plugins, :limit_posts,
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
Expand Down Expand Up @@ -84,6 +84,7 @@ def print_stats
Jekyll.logger.info @liquid_renderer.stats_table
end

# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
#
# Reset Site details.
Expand All @@ -96,6 +97,7 @@ def reset
Time.now
end
self.layouts = {}
self.inclusions = {}
self.pages = []
self.static_files = []
self.data = {}
Expand All @@ -115,6 +117,7 @@ def reset
Jekyll::Hooks.trigger :site, :after_reset, self
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize

# Load necessary libraries, plugins, converters, and generators.
#
Expand Down
61 changes: 58 additions & 3 deletions lib/jekyll/tags/include.rb
Expand Up @@ -18,12 +18,13 @@ class IncludeTag < Liquid::Tag

def initialize(tag_name, markup, tokens)
super
matched = markup.strip.match(VARIABLE_SYNTAX)
markup = markup.strip
matched = markup.match(VARIABLE_SYNTAX)
if matched
@file = matched["variable"].strip
@params = matched["params"].strip
else
@file, @params = markup.strip.split(%r!\s+!, 2)
@file, @params = markup.split(%r!\s+!, 2)
end
validate_params if @params
@tag_name = tag_name
Expand Down Expand Up @@ -192,6 +193,60 @@ def could_not_locate_message(file, includes_dirs, safe)
end
end

# Do not inherit from this class.
# TODO: Merge into the `Jekyll::Tags::IncludeTag` in v5.0
class OptimizedIncludeTag < IncludeTag
def render(context)
@site ||= context.registers[:site]

file = render_variable(context) || @file
validate_file_name(file)

@site.inclusions[file] ||= locate_include_file(file)
inclusion = @site.inclusions[file]

add_include_to_dependency(inclusion, context) if @site.incremental?

context.stack do
context["include"] = parse_params(context) if @params
inclusion.render(context)
end
end

private

def locate_include_file(file)
@site.includes_load_paths.each do |dir|
path = PathManager.join(dir, file)
return Inclusion.new(@site, dir, file) if valid_include_file?(path, dir)
end
raise IOError, could_not_locate_message(file, @site.includes_load_paths, @site.safe)
end

def valid_include_file?(path, dir)
File.file?(path) && !outside_scope?(path, dir)
end

def outside_scope?(path, dir)
@site.safe && !realpath_prefixed_with?(path, dir)
end

def realpath_prefixed_with?(path, dir)
File.realpath(path).start_with?(dir)
rescue StandardError
false
end

def add_include_to_dependency(inclusion, context)
return unless context.registers[:page]&.key?("path")

@site.regenerator.add_dependency(
@site.in_source_dir(context.registers[:page]["path"]),
inclusion.path
)
end
end

class IncludeRelativeTag < IncludeTag
def tag_includes_dirs(context)
Array(page_path(context)).freeze
Expand All @@ -217,5 +272,5 @@ def page_path(context)
end
end

Liquid::Template.register_tag("include", Jekyll::Tags::IncludeTag)
Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)