class Jekyll::FrontmatterDefaults

It is exposed via the frontmatter_defaults method on the site class.
and the data available to liquid.
These are set in _config.yml and apply both to internal use (e.g. layout)
This class handles custom defaults for YAML frontmatter settings.

def all(path, type)

Returns a hash with all default values (an empty hash if there are none)

type - a symbol indicating the type (:post, :page or :draft)
path - the relative path of the page or post

Collects a hash with all default values for a page or post
def all(path, type)
  defaults = {}
  old_scope = nil
  matching_sets(path, type).each do |set|
    if has_precedence?(old_scope, set["scope"])
      defaults = Utils.deep_merge_hashes(defaults, set["values"])
      old_scope = set["scope"]
    else
      defaults = Utils.deep_merge_hashes(set["values"], defaults)
    end
  end
  defaults
end

def applies?(scope, path, type)

Returns true if the scope applies to the given type and path

type - the type (:post, :page or :draft) to check for
path - the path to check for
scope - the hash indicating the scope, as defined in _config.yml

Checks if a given default setting scope matches the given path and type
def applies?(scope, path, type)
  applies_type?(scope, type) && applies_path?(scope, path)
end

def applies_path?(scope, path)

def applies_path?(scope, path)
  rel_scope_path = scope["path"]
  return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty?
  sanitized_path = sanitize_path(path)
  if rel_scope_path.include?("*")
    glob_scope(sanitized_path, rel_scope_path)
  else
    path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
  end
end

def applies_type?(scope, type)

otherwise returns false
Returns true if either of the above conditions are satisfied,

its defaults.
type - the type of the document being processed / asked about
scope - the Hash defaults set being asked about application

2. the 'type' in the scope is the same as the type asked about
1. no 'type' is specified
The scope applies to the type if:
Determines whether the scope applies to type.
def applies_type?(scope, type)
  !scope.key?("type") || type&.to_sym.eql?(scope["type"].to_sym)
end

def ensure_time!(set)

def ensure_time!(set)
  return set unless set.key?("values") && set["values"].key?("date")
  return set if set["values"]["date"].is_a?(Time)
  set["values"]["date"] = Utils.parse_date(
    set["values"]["date"],
    "An invalid date format was found in a front-matter default set: #{set}"
  )
  set
end

def find(path, type, setting)

Returns the default value or nil if none was found

a :post or a :draft calls this method
type - a symbol indicating whether a :page,
post or :draft the default is used in
path - the path (relative to the source) of the page,

Finds a default value for a given setting, filtered by path and type
def find(path, type, setting)
  value = nil
  old_scope = nil
  matching_sets(path, type).each do |set|
    if set["values"].key?(setting) && has_precedence?(old_scope, set["scope"])
      value = set["values"][setting]
      old_scope = set["scope"]
    end
  end
  value
end

def glob_cache(path)

def glob_cache(path)
  @glob_cache ||= {}
  @glob_cache[path] ||= Dir.glob(path)
end

def glob_scope(sanitized_path, rel_scope_path)

def glob_scope(sanitized_path, rel_scope_path)
  site_source    = Pathname.new(@site.source)
  abs_scope_path = site_source.join(rel_scope_path).to_s
  glob_cache(abs_scope_path).each do |scope_path|
    scope_path = Pathname.new(scope_path).relative_path_from(site_source).to_s
    scope_path = strip_collections_dir(scope_path)
    Jekyll.logger.debug "Globbed Scope Path:", scope_path
    return true if path_is_subpath?(sanitized_path, scope_path)
  end
  false
end

def has_precedence?(old_scope, new_scope)

rubocop: disable Naming/PredicateName
Returns true if the new scope has precedence over the older

new_scope - the new scope hash
old_scope - the old scope hash, or nil if there's none

Determines if a new scope has precedence over an old one
def has_precedence?(old_scope, new_scope)
  return true if old_scope.nil?
  new_path = sanitize_path(new_scope["path"])
  old_path = sanitize_path(old_scope["path"])
  if new_path.length != old_path.length
    new_path.length >= old_path.length
  elsif new_scope.key?("type")
    true
  else
    !old_scope.key? "type"
  end
end

def initialize(site)

Initializes a new instance.
def initialize(site)
  @site = site
end

def matching_sets(path, type)

Returns an array of hashes

Collects a list of sets that match the given path and type
def matching_sets(path, type)
  @matched_set_cache ||= {}
  @matched_set_cache[path] ||= {}
  @matched_set_cache[path][type] ||= valid_sets.select do |set|
    !set.key?("scope") || applies?(set["scope"], path, type)
  end
end

def path_is_subpath?(path, parent_path)

def path_is_subpath?(path, parent_path)
  path.start_with?(parent_path)
end

def reset

def reset
  @glob_cache = {} if @glob_cache
end

def sanitize_path(path)

Sanitizes the given path by removing a leading slash
def sanitize_path(path)
  if path.nil? || path.empty?
    ""
  elsif path.start_with?("/")
    path.gsub(%r!\A/|(?<=[^/])\z!, "")
  else
    path
  end
end

def strip_collections_dir(path)

def strip_collections_dir(path)
  collections_dir  = @site.config["collections_dir"]
  slashed_coll_dir = collections_dir.empty? ? "/" : "#{collections_dir}/"
  return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir)
  path.sub(slashed_coll_dir, "")
end

def update_deprecated_types(set)

def update_deprecated_types(set)
  return set unless set.key?("scope") && set["scope"].key?("type")
  set["scope"]["type"] =
    case set["scope"]["type"]
    when "page"
      Deprecator.defaults_deprecate_type("page", "pages")
      "pages"
    when "post"
      Deprecator.defaults_deprecate_type("post", "posts")
      "posts"
    when "draft"
      Deprecator.defaults_deprecate_type("draft", "drafts")
      "drafts"
    else
      set["scope"]["type"]
    end
  set
end

def valid?(set)

Returns true if the set is valid and can be used in this class

set - the default value hash, as defined in _config.yml

Checks if a given set of default values is valid
def valid?(set)
  set.is_a?(Hash) && set["values"].is_a?(Hash)
end

def valid_sets

Returns an array of hashes

and have their changes take effect
This is not cached to allow plugins to modify the configuration

Returns a list of valid sets
def valid_sets
  sets = @site.config["defaults"]
  return [] unless sets.is_a?(Array)
  sets.map do |set|
    if valid?(set)
      ensure_time!(update_deprecated_types(set))
    else
      Jekyll.logger.warn "Defaults:", "An invalid front-matter default set was found:"
      Jekyll.logger.warn set.to_s
      nil
    end
  end.tap(&:compact!)
end