按官网说法,应该会比较靠前执行:

In your site source root, make a _plugins directory. Place your plugins here. Any file ending in *.rb inside this directory will be loaded before Jekyll generates your site

验证一下,先搜搜“_plugins”

➜  jekyll  grep '_plugins' -Rn ./
./configuration.rb:12:      'plugins'       => '_plugins',
./plugin_manager.rb:38:        required_gems = Bundler.require(:jekyll_plugins) # requires the gems in this group only


进configuration.rb看看,它还有个哈希常量叫DEFAULTS

module Jekyll
  class Configuration < Hash

    # Default options. Overridden by values in _config.yml.
    # Strings rather than symbols are used for compatibility with YAML.
    DEFAULTS = {
      # Where things are
      'source'        => Dir.pwd,
      'destination'   => File.join(Dir.pwd, '_site'),
      'plugins'       => '_plugins',
      'layouts'       => '_layouts',
      'data_source'   =>  '_data',
      'collections'   => nil,


搜搜DEFAULTS,看样子应该到plugin_manager.rb看看

➜  jekyll  grep 'DEFAULTS' -Rn ./
./configuration.rb:8:    DEFAULTS = {
./configuration.rb:112:      override['source'] || self['source'] || DEFAULTS['source']
./configuration.rb:116:      override['quiet'] || self['quiet'] || DEFAULTS['quiet']
./plugin_manager.rb:88:      if (site.config['plugins'] == Jekyll::Configuration::DEFAULTS['plugins'])


发现plugin_manager.rb中,site.config['plugins']的site,是new时赋予的

module Jekyll
  class PluginManager
    attr_reader :site

    # Create an instance of this class.
    #
    # site - the instance of Jekyll::Site we're concerned with
    #
    # Returns nothing
    def initialize(site)
      @site = site
    end


于是查查PluginManager.new

➜  jekyll  grep 'PluginManager.new' -Rn ./
./site.rb:30:      self.plugin_manager = Jekyll::PluginManager.new(self)


到site.rb看看,它持有一个plugin_manager,并且在new时,需要传入config

module Jekyll
  class Site
    attr_reader   :source, :dest, :config
    attr_accessor :layouts, :posts, :pages, :static_files,
                  :exclude, :include, :lsi, :highlighter, :permalink_style,
                  :time, :future, :unpublished, :safe, :plugins, :limit_posts,
                  :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
                  :gems, :plugin_manager

    attr_accessor :converters, :generators

    # Public: Initialize a new Site.
    #
    # config - A Hash containing site configuration details.
    def initialize(config)
      @config = config.clone

      %w[safe lsi highlighter baseurl exclude include future unpublished
        show_drafts limit_posts keep_files gems].each do |opt|
        self.send("#{opt}=", config[opt])
      end

      # Source and destination may not be changed after the site has been created.
      @source              = File.expand_path(config['source']).freeze
      @dest                = File.expand_path(config['destination']).freeze

      self.plugin_manager = Jekyll::PluginManager.new(self)
      self.plugins        = plugin_manager.plugins_path


查查Site.new

➜  jekyll  grep 'Site.new' -Rn ./
./commands/doctor.rb:21:          site = Jekyll::Site.new(configuration_from_options(options))
./commands/build.rb:29:          site = Jekyll::Site.new(options)


到commands/build.rb看看,其中init_with_program是jekyll命令行的一系列钩子中的一环,当命令行执行“jekyll build”时,最终会执行“Jekyll::Commands::Build.process(options) ”

module Jekyll
  module Commands
    class Build < Command

      class << self

        # Create the Mercenary command for the Jekyll CLI for this Command
        def init_with_program(prog)
          prog.command(:build) do |c|
            c.syntax      'build [options]'
            c.description 'Build your site'
            c.alias :b

            add_build_options(c)

            c.action do |args, options|
              options["serving"] = false
              Jekyll::Commands::Build.process(options)
            end
          end
        end

        # Build your jekyll site
        # Continuously watch if `watch` is set to true in the config.
        def process(options)
          Jekyll.logger.log_level = :error if options['quiet']

          options = configuration_from_options(options)
          site = Jekyll::Site.new(options)


Site.new时传入的options,先经configuration_from_options转换,此方法来自于父类Command,去看看

# Create a full Jekyll configuration with the options passed in as overrides
#
# options - the configuration overrides
#
# Returns a full Jekyll configuration
def configuration_from_options(options)
  Jekyll.configuration(options)
end


至此更明显了,它就是用于覆盖上面看到的DEFAULTS配置的。它引用了Jekyll.configuration来做,去看看

def configuration(override = Hash.new)
  config = Configuration[Configuration::DEFAULTS]
  override = Configuration[override].stringify_keys
  unless override.delete('skip_config_files')
    config = config.read_config_files(config.config_files(override))
  end

  # Merge DEFAULTS < _config.yml < override
  config = Utils.deep_merge_hashes(config, override).stringify_keys
  set_timezone(config['timezone']) if config['timezone']

  config
end