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 11 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
60 changes: 60 additions & 0 deletions lib/jekyll/inclusion.rb
@@ -0,0 +1,60 @@
# frozen_string_literal: true

module Jekyll
class Inclusion
attr_reader :name, :path

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

def relative_path
ashmaroli marked this conversation as resolved.
Show resolved Hide resolved
@relative_path ||= compute_relative_path
end

def render(context)
@template ||= site.liquid_renderer.file(relative_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} #{relative_path.inspect}"
end
alias_method :to_s, :inspect

private

attr_reader :site

def dir_path
if site.theme && path.start_with?(site.theme.includes_path)
ashmaroli marked this conversation as resolved.
Show resolved Hide resolved
PathManager.join site.theme.basename, "_includes"
else
site.config["includes_dir"]
end
end

# rubocop:disable Metrics/AbcSize
def compute_relative_path
case path
when site.theme && %r!#{site.theme.includes_path}/(?<rel_path>.*)!o
PathManager.join dir_path, Regexp.last_match[:rel_path]
when %r!#{site.in_source_dir(site.config["includes_dir"], "/")}(?<rel_path>.*)!o
PathManager.join dir_path, Regexp.last_match[:rel_path]
else
path.sub(site.in_source_dir("/"), "")
end
end
# rubocop:enable Metrics/AbcSize
end
ashmaroli marked this conversation as resolved.
Show resolved Hide resolved
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.exist?(path) && !File.directory?(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)