module Jekyll::Watcher

Public Instance Methods

watch(options, site = nil) click to toggle source

Public: Continuously watch for file changes and rebuild the site whenever a change is detected.

If the optional site argument is populated, that site instance will be reused and the options Hash ignored. Otherwise, a new site instance will be instantiated from the options Hash and used.

options - A Hash containing the site configuration site - The current site instance (populated starting with Jekyll 3.2)

(optional, default: nil)

Returns nothing.

# File lib/jekyll/watcher.rb, line 20
def watch(options, site = nil)
  ENV["LISTEN_GEM_DEBUGGING"] ||= "1" if options["verbose"]

  site ||= Jekyll::Site.new(options)
  listener = build_listener(site, options)
  listener.start

  Jekyll.logger.info "Auto-regeneration:", "enabled for '#{options["source"]}'"

  unless options["serving"]
    trap("INT") do
      listener.stop
      puts "     Halting auto-regeneration."
      exit 0
    end

    sleep_forever
  end
rescue ThreadError
  # You pressed Ctrl-C, oh my!
end

Private Instance Methods

build_listener(site, options) click to toggle source
# File lib/jekyll/watcher.rb, line 43
def build_listener(site, options)
  Listen.to(
    options["source"],
    :ignore        => listen_ignore_paths(options),
    :force_polling => options["force_polling"],
    &listen_handler(site)
  )
end
config_files(options) click to toggle source
# File lib/jekyll/watcher.rb, line 75
def config_files(options)
  %w(yml yaml toml).map do |ext|
    Jekyll.sanitized_path(options["source"], "_config.#{ext}")
  end
end
custom_excludes(options) click to toggle source
# File lib/jekyll/watcher.rb, line 70
def custom_excludes(options)
  Array(options["exclude"]).map { |e| Jekyll.sanitized_path(options["source"], e) }
end
listen_handler(site) click to toggle source
# File lib/jekyll/watcher.rb, line 53
def listen_handler(site)
  proc do |modified, added, removed|
    t = Time.now
    c = modified + added + removed
    n = c.length
    Jekyll.logger.info "Regenerating:",
      "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}"

    c.map { |path| path.sub("#{site.source}/", "") }.each do |file|
      Jekyll.logger.info "", file
    end

    process(site, t)
  end
end
listen_ignore_paths(options) click to toggle source

Paths to ignore for the watch option

options - A Hash of options passed to the command

Returns a list of relative paths from source that should be ignored

# File lib/jekyll/watcher.rb, line 96
def listen_ignore_paths(options)
  source       = Pathname.new(options["source"]).expand_path
  paths        = to_exclude(options)

  paths.map do |p|
    absolute_path = Pathname.new(p).expand_path
    next unless absolute_path.exist?
    begin
      relative_path = absolute_path.relative_path_from(source).to_s
      unless relative_path.start_with?("../")
        path_to_ignore = Regexp.new(Regexp.escape(relative_path))
        Jekyll.logger.debug "Watcher:", "Ignoring #{path_to_ignore}"
        path_to_ignore
      end
    rescue ArgumentError
      # Could not find a relative path
    end
  end.compact + [%r!\.jekyll\-metadata!]
end
process(site, time) click to toggle source
# File lib/jekyll/watcher.rb, line 122
def process(site, time)
  begin
    site.process
    Jekyll.logger.info "", "...done in #{Time.now - time} seconds."
  rescue => e
    Jekyll.logger.warn "Error:", e.message
    Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information."
  end
  puts ""
end
sleep_forever() click to toggle source
# File lib/jekyll/watcher.rb, line 117
def sleep_forever
  loop { sleep 1000 }
end
to_exclude(options) click to toggle source
# File lib/jekyll/watcher.rb, line 82
def to_exclude(options)
  [
    config_files(options),
    options["destination"],
    custom_excludes(options),
  ].flatten
end