├── .gitignore
├── .rspec
├── .rubocop.yml
├── .rubocop_todo.yml
├── .travis.yml
├── Gemfile
├── History.markdown
├── LICENSE.txt
├── README.md
├── Rakefile
├── appveyor.yml
├── jekyll-watch.gemspec
├── lib
├── jekyll-watch.rb
├── jekyll-watch
│ └── version.rb
└── jekyll
│ ├── commands
│ └── watch.rb
│ └── watcher.rb
├── script
├── bootstrap
├── cibuild
├── fmt
├── release
├── test
└── test-watcher
└── spec
├── spec_helper.rb
├── test-sité
├── .gitignore
├── _config.dev.toml
├── _config.yml
├── _includes
│ ├── footer.html
│ ├── head.html
│ └── header.html
├── _layouts
│ ├── default.html
│ ├── page.html
│ └── post.html
├── _posts
│ ├── 2014-08-08-welcome-to-jekyll.markdown
│ └── 2014-08-08-歡迎使用jekyll.markdown
├── _sass
│ ├── _base.scss
│ ├── _layout.scss
│ └── _syntax-highlighting.scss
├── about.md
├── css
│ └── main.scss
├── feed.xml
└── index.html
└── watcher_spec.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | *.bundle
19 | *.so
20 | *.o
21 | *.a
22 | mkmf.log
23 | vendor
24 |
25 | .sass-cache
26 | _site
27 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: .rubocop_todo.yml
2 |
3 | require: rubocop-jekyll
4 | inherit_gem:
5 | rubocop-jekyll: .rubocop.yml
6 |
7 | AllCops:
8 | TargetRubyVersion: 2.3
9 | Include:
10 | - lib/**/*.rb
11 |
12 | Exclude:
13 | - vendor/**/*
14 |
--------------------------------------------------------------------------------
/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 | # This configuration was generated by
2 | # `rubocop --auto-gen-config --auto-gen-only-exclude`
3 | # on 2020-03-19 22:20:35 +0100 using RuboCop version 0.80.1.
4 | # The point is for the user to remove these configuration records
5 | # one by one as the offenses are removed from the code base.
6 | # Note that changes in the inspected code, or installation of new
7 | # versions of RuboCop, may require this file to be generated again.
8 |
9 | # Offense count: 2
10 | # Configuration parameters: AllowComments.
11 | Lint/SuppressedException:
12 | Exclude:
13 | - 'lib/jekyll/watcher.rb'
14 |
15 | # Offense count: 1
16 | # Configuration parameters: Max.
17 | Metrics/AbcSize:
18 | Exclude:
19 | - 'lib/jekyll/watcher.rb'
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | cache: bundler
3 | rvm:
4 | - &ruby1 2.7
5 | - &ruby2 2.5
6 | matrix:
7 | include:
8 | - rvm: *ruby1
9 | env: JEKYLL_VERSION="~> 4.0"
10 | - rvm: *ruby1
11 | env: JEKYLL_VERSION="~> 3.9"
12 | branches:
13 | only:
14 | - master
15 |
16 | before_install:
17 | - gem update --system
18 | - gem install bundler
19 | install:
20 | - travis_retry script/bootstrap
21 | script: script/cibuild
22 |
23 | notifications:
24 | irc:
25 | on_success: change
26 | on_failure: change
27 | channels:
28 | - irc.freenode.org#jekyll
29 | template:
30 | - '%{repository}#%{build_number} %{message} %{build_url}'
31 | email:
32 | on_success: never
33 | on_failure: change
34 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 | gemspec
5 |
6 | gem "jekyll", ENV["JEKYLL_VERSION"] if ENV["JEKYLL_VERSION"]
7 | gem "kramdown-parser-gfm" if ENV["JEKYLL_VERSION"] == "~> 3.9"
8 |
--------------------------------------------------------------------------------
/History.markdown:
--------------------------------------------------------------------------------
1 | ## 2.2.1 / 2019-03-22
2 |
3 | ### Bug Fixes
4 |
5 | * Fix encoding discrepancy in excluded Windows paths (#76)
6 | * Ignore directories rather than all similar paths (#65)
7 |
8 | ### Development Fixes
9 |
10 | * Test against Ruby 2.6
11 | * Relax version constraint on bundler to allow using 1.x or 2.x
12 | * dependencies: rubocop-jekyll 0.5
13 | * style: target Ruby 2.4
14 |
15 | ## 2.2.0 (YANKED)
16 |
17 | ## 2.1.2 / 2018-10-17
18 |
19 | ### Development Fixes
20 |
21 | * Initialize AppVeyor CI to test plugin on Windows (#77)
22 |
23 | ### Bug Fixes
24 |
25 | * Fix watcher failure due to incorrect file name encoding (#78)
26 |
27 | ## 2.1.1 / 2018-10-10
28 |
29 | ### Bug Fixes
30 |
31 | * Replace non-existent local variable (#73)
32 |
33 | ## 2.1.0 / 2018-10-09
34 |
35 | ### Bug Fixes
36 |
37 | * Normalize watched-path encoding (#69)
38 |
39 | ### Development Fixes
40 |
41 | * Test against Ruby 2.5 (#62)
42 | * Drop support for Ruby 2.2 (EOL)
43 | * Style: lint with rubocop-jekyll
44 |
45 | ## 2.0.0 / 2016-12-02
46 |
47 | ### Development Fixes
48 |
49 | * Update versions for Travis (#43)
50 | * Define path with __dir__ (#48)
51 | * Remove version lock for dependency listen (#50)
52 | * Inherit Jekyll's rubocop config for consistency (#51)
53 | * Update jekyll-watch (#53)
54 | * Drop support for old Ruby and old Jekyll (#55)
55 |
56 | ### Minor Enhancements
57 |
58 | * Output regenerated file paths to terminal (#57)
59 |
60 | ### Major Enhancements
61 |
62 | * Remove unnecessary method (#56)
63 |
64 | ## 1.5.0 / 2016-07-20
65 |
66 | * reuse provided site instance if available (#40)
67 |
68 | ## 1.4.0 / 2016-04-25
69 |
70 | * Lock Listen to less than 3.1. (#38)
71 |
72 | ## 1.3.1 / 2016-01-19
73 |
74 | * Test against Jekyll 2 and 3. (#30)
75 | * watcher: set `LISTEN_GEM_DEBUGGING` if `--verbose` flag set (#31)
76 | * Apply Rubocop auditing and fix up (#32)
77 |
78 | ## 1.3.0 / 2015-09-23
79 |
80 | * Lock to Listen 3.x (#25)
81 |
82 | ## 1.2.1 / 2015-01-24
83 |
84 | * Show regen time & use the same `Site` object across regens (#21)
85 |
86 | ## 1.2.0 / 2014-12-05
87 |
88 | * *Always* ignore `.jekyll-metadata`, even if it doesn't exist. (#18)
89 | * Ignore `.jekyll-metadata` by default if it exists (#15)
90 |
91 | ## 1.1.2 / 2014-11-08
92 |
93 | * Only ignore a file or directory if it exists (#13)
94 |
95 | ## 1.1.1 / 2014-09-05
96 |
97 | * Exclude test files from the gem build (#9)
98 |
99 | ## 1.1.0 / 2014-08-10
100 |
101 | ### Minor Enhancements
102 |
103 | * Refactor the whole watching thing and compartmentalize it. (#5)
104 | * Don't listen to things in the `exclude` configuration option. (#5)
105 |
106 | ### Development Fixes
107 |
108 | * Add github stuff and the beginnings of the test suite (#6)
109 | * Flesh out the test suite (#7)
110 |
111 | ## 1.0.0 / 2014-06-27
112 |
113 | * Birthday!
114 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-present Parker Moore and the jekyll-watch contributors
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Jekyll Watch
2 |
3 | Rebuild your Jekyll site when a file changes with the `--watch` switch.
4 |
5 | [][travis]
6 | [][appveyor]
7 |
8 | [travis]: https://travis-ci.org/jekyll/jekyll-watch
9 | [appveyor]: https://ci.appveyor.com/project/jekyll/jekyll-watch
10 |
11 | ## Installation
12 |
13 | **`jekyll-watch` comes pre-installed with Jekyll 2.1 or greater.**
14 |
15 | Add this line to your application's Gemfile:
16 |
17 | gem 'jekyll-watch'
18 |
19 | And then execute:
20 |
21 | $ bundle
22 |
23 | Or install it yourself as:
24 |
25 | $ gem install jekyll-watch
26 |
27 | ## Usage
28 |
29 | Pass the `--watch` flag to `jekyll build` or `jekyll serve`:
30 |
31 | ```bash
32 | $ jekyll build --watch
33 | $ jekyll serve --watch # this flag is the default, so no need to specify it here for the 'serve' command
34 | ```
35 |
36 | The `--watch` flag can be used in combination with any other flags for those
37 | two commands, except `--detach` for the `serve` command.
38 |
39 | ## Contributing
40 |
41 | 1. Fork it ( https://github.com/jekyll/jekyll-watch/fork )
42 | 2. Create your feature branch (`git checkout -b my-new-feature`)
43 | 3. Commit your changes (`git commit -am 'Add some feature'`)
44 | 4. Push to the branch (`git push origin my-new-feature`)
45 | 5. Create a new Pull Request
46 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/gem_tasks"
4 |
5 | require "rspec/core/rake_task"
6 |
7 | RSpec::Core::RakeTask.new(:spec) do |t|
8 | t.verbose = false
9 | end
10 |
11 | task :default => :spec
12 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: "{build}"
2 | clone_depth: 5
3 | branches:
4 | only:
5 | - master
6 |
7 | build: off
8 |
9 | environment:
10 | matrix:
11 | - RUBY_FOLDER_VER: "26"
12 | JEKYLL_VERSION : "~> 4.0"
13 | - RUBY_FOLDER_VER: "26"
14 | JEKYLL_VERSION : "~> 3.9"
15 | - RUBY_FOLDER_VER: "25"
16 | JEKYLL_VERSION : "~> 4.0"
17 | - RUBY_FOLDER_VER: "25"
18 | JEKYLL_VERSION : "~> 3.9"
19 |
20 | install:
21 | - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH%
22 | - bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle
23 |
24 | test_script:
25 | - ruby --version
26 | - gem --version
27 | - bundler --version
28 | - bundle exec rspec
29 |
30 | cache:
31 | # If one of the files after the right arrow changes, cache will be invalidated
32 | - 'vendor\bundle -> appveyor.yml,Gemfile,jekyll-watch.gemspec'
33 |
--------------------------------------------------------------------------------
/jekyll-watch.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "lib/jekyll-watch/version"
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = "jekyll-watch"
7 | spec.version = Jekyll::Watch::VERSION
8 | spec.authors = ["Parker Moore"]
9 | spec.email = ["parkrmoore@gmail.com"]
10 | spec.summary = "Rebuild your Jekyll site when a file changes with the `--watch` switch."
11 | spec.homepage = "https://github.com/jekyll/jekyll-watch"
12 | spec.license = "MIT"
13 |
14 | spec.files = `git ls-files -z`.split("\x0").grep(%r!(bin|lib)/!)
15 | spec.executables = spec.files.grep(%r!^bin/!) { |f| File.basename(f) }
16 | spec.require_paths = ["lib"]
17 |
18 | spec.required_ruby_version = ">= 2.3.0"
19 |
20 | spec.add_runtime_dependency "listen", "~> 3.0"
21 |
22 | require "rbconfig"
23 | if RbConfig::CONFIG["host_os"] =~ %r!mswin|mingw|cygwin!
24 | spec.add_runtime_dependency "wdm", "~> 0.1.0"
25 | end
26 |
27 | spec.add_development_dependency "bundler"
28 | spec.add_development_dependency "jekyll", ">= 3.6", "< 5.0"
29 | spec.add_development_dependency "rake"
30 | spec.add_development_dependency "rspec", "~> 3.0"
31 | spec.add_development_dependency "rubocop-jekyll", "~> 0.11"
32 | end
33 |
--------------------------------------------------------------------------------
/lib/jekyll-watch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "jekyll-watch/version"
4 | require_relative "jekyll/watcher"
5 | require_relative "jekyll/commands/watch"
6 |
--------------------------------------------------------------------------------
/lib/jekyll-watch/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Jekyll
4 | module Watch
5 | VERSION = "2.2.1"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/jekyll/commands/watch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Jekyll
4 | module Commands
5 | module Watch
6 | extend self
7 |
8 | def init_with_program(prog); end
9 |
10 | # Build your jekyll site
11 | # Continuously watch if `watch` is set to true in the config.
12 | def process(options)
13 | Jekyll.logger.log_level = :error if options["quiet"]
14 | Jekyll::Watcher.watch(options) if options["watch"]
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/jekyll/watcher.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "listen"
4 |
5 | module Jekyll
6 | module Watcher
7 | extend self
8 |
9 | # Public: Continuously watch for file changes and rebuild the site
10 | # whenever a change is detected.
11 | #
12 | # If the optional site argument is populated, that site instance will be
13 | # reused and the options Hash ignored. Otherwise, a new site instance will
14 | # be instantiated from the options Hash and used.
15 | #
16 | # options - A Hash containing the site configuration
17 | # site - The current site instance (populated starting with Jekyll 3.2)
18 | # (optional, default: nil)
19 | #
20 | # Returns nothing.
21 | def watch(options, site = nil)
22 | ENV["LISTEN_GEM_DEBUGGING"] ||= "1" if options["verbose"]
23 |
24 | site ||= Jekyll::Site.new(options)
25 | listener = build_listener(site, options)
26 | listener.start
27 |
28 | Jekyll.logger.info "Auto-regeneration:", "enabled for '#{options["source"]}'"
29 |
30 | unless options["serving"]
31 | trap("INT") do
32 | listener.stop
33 | Jekyll.logger.info "", "Halting auto-regeneration."
34 | exit 0
35 | end
36 |
37 | sleep_forever
38 | end
39 | rescue ThreadError
40 | # You pressed Ctrl-C, oh my!
41 | end
42 |
43 | private
44 |
45 | def build_listener(site, options)
46 | Listen.to(
47 | options["source"],
48 | :ignore => listen_ignore_paths(options),
49 | :force_polling => options["force_polling"],
50 | &listen_handler(site)
51 | )
52 | end
53 |
54 | def listen_handler(site)
55 | proc do |modified, added, removed|
56 | t = Time.now
57 | c = modified + added + removed
58 | n = c.length
59 |
60 | Jekyll.logger.info "Regenerating:",
61 | "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}"
62 |
63 | c.each { |path| Jekyll.logger.info "", path["#{site.source}/".length..-1] }
64 | process(site, t)
65 | end
66 | end
67 |
68 | def normalize_encoding(obj, desired_encoding)
69 | case obj
70 | when Array
71 | obj.map { |entry| entry.encode!(desired_encoding, entry.encoding) }
72 | when String
73 | obj.encode!(desired_encoding, obj.encoding)
74 | end
75 | end
76 |
77 | def custom_excludes(options)
78 | Array(options["exclude"]).map { |e| Jekyll.sanitized_path(options["source"], e) }
79 | end
80 |
81 | def config_files(options)
82 | %w(yml yaml toml).map do |ext|
83 | Jekyll.sanitized_path(options["source"], "_config.#{ext}")
84 | end
85 | end
86 |
87 | def to_exclude(options)
88 | [
89 | config_files(options),
90 | options["destination"],
91 | custom_excludes(options),
92 | ].flatten
93 | end
94 |
95 | # Paths to ignore for the watch option
96 | #
97 | # options - A Hash of options passed to the command
98 | #
99 | # Returns a list of relative paths from source that should be ignored
100 | def listen_ignore_paths(options)
101 | source = Pathname.new(options["source"]).expand_path
102 | paths = to_exclude(options)
103 |
104 | paths.map do |p|
105 | absolute_path = Pathname.new(normalize_encoding(p, options["source"].encoding)).expand_path
106 | next unless absolute_path.exist?
107 |
108 | begin
109 | relative_path = absolute_path.relative_path_from(source).to_s
110 | relative_path = File.join(relative_path, "") if absolute_path.directory?
111 | unless relative_path.start_with?("../")
112 | path_to_ignore = %r!^#{Regexp.escape(relative_path)}!
113 | Jekyll.logger.debug "Watcher:", "Ignoring #{path_to_ignore}"
114 | path_to_ignore
115 | end
116 | rescue ArgumentError
117 | # Could not find a relative path
118 | end
119 | end.compact + [%r!^\.jekyll\-metadata!]
120 | end
121 |
122 | def sleep_forever
123 | loop { sleep 1000 }
124 | end
125 |
126 | def process(site, time)
127 | begin
128 | site.process
129 | Jekyll.logger.info "", "...done in #{Time.now - time} seconds."
130 | rescue StandardError => e
131 | Jekyll.logger.warn "Error:", e.message
132 | Jekyll.logger.warn "Error:", "Run jekyll build --trace for more information."
133 | end
134 | Jekyll.logger.info ""
135 | end
136 | end
137 | end
138 |
--------------------------------------------------------------------------------
/script/bootstrap:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | if [[ "$TRAVIS" == "true" ]]; then
4 | echo "We're on Travis! Installing to vendor."
5 | time bundle install --path vendor
6 | else
7 | bundle install --system
8 | fi
9 |
--------------------------------------------------------------------------------
/script/cibuild:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | time script/test
4 | time script/fmt
5 |
--------------------------------------------------------------------------------
/script/fmt:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | bundle exec rubocop -D -E
3 |
--------------------------------------------------------------------------------
/script/release:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | rake release
4 |
--------------------------------------------------------------------------------
/script/test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | bundle exec rspec "$@"
3 |
--------------------------------------------------------------------------------
/script/test-watcher:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | local-jekyll serve --watch \
4 | --source spec/test-sité \
5 | --destination spec/test-sité/_site
6 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "jekyll"
4 | require File.expand_path("../lib/jekyll-watch.rb", __dir__)
5 | TEST_DIR = __dir__
6 |
7 | RSpec.configure do |config|
8 | # These two settings work together to allow you to limit a spec run
9 | # to individual examples or groups you care about by tagging them with
10 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples
11 | # get run.
12 | config.filter_run :focus
13 | config.run_all_when_everything_filtered = true
14 |
15 | # Many RSpec users commonly either run the entire suite or an individual
16 | # file, and it's useful to allow more verbose output when running an
17 | # individual spec file.
18 | if config.files_to_run.one?
19 | # Use the documentation formatter for detailed output,
20 | # unless a formatter has already been configured
21 | # (e.g. via a command-line flag).
22 | config.default_formatter = "doc"
23 | end
24 |
25 | # Print the 10 slowest examples and example groups at the
26 | # end of the spec run, to help surface which specs are running
27 | # particularly slow.
28 | config.profile_examples = 10
29 |
30 | # Run specs in random order to surface order dependencies. If you find an
31 | # order dependency and want to debug it, you can fix the order by providing
32 | # the seed, which is printed after each run.
33 | # --seed 1234
34 | config.order = :random
35 |
36 | # Seed global randomization in this process using the `--seed` CLI option.
37 | # Setting this allows you to use `--seed` to deterministically reproduce
38 | # test failures related to randomization by passing the same `--seed` value
39 | # as the one that triggered the failure.
40 | Kernel.srand config.seed
41 |
42 | # rspec-expectations config goes here. You can use an alternate
43 | # assertion/expectation library such as wrong or the stdlib/minitest
44 | # assertions if you prefer.
45 | config.expect_with :rspec do |expectations|
46 | # Enable only the newer, non-monkey-patching expect syntax.
47 | # For more details, see:
48 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
49 | expectations.syntax = :expect
50 | end
51 |
52 | # rspec-mocks config goes here. You can use an alternate test double
53 | # library (such as bogus or mocha) by changing the `mock_with` option here.
54 | config.mock_with :rspec do |mocks|
55 | # Enable only the newer, non-monkey-patching expect syntax.
56 | # For more details, see:
57 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
58 | mocks.syntax = :expect
59 |
60 | # Prevents you from mocking or stubbing a method that does not exist on
61 | # a real object. This is generally recommended.
62 | mocks.verify_partial_doubles = true
63 | end
64 |
65 | def test_dir(*files)
66 | File.join(TEST_DIR, *files)
67 | end
68 |
69 | def source_dir(*files)
70 | test_dir("test-sité", *files)
71 | end
72 |
73 | def dest_dir(*files)
74 | source_dir("_site", *files)
75 | end
76 |
77 | def ignore_path?(patterns, path)
78 | path = path.to_s
79 | patterns.each do |pattern|
80 | return true if path =~ pattern
81 | next
82 | end
83 | false
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/spec/test-sité/.gitignore:
--------------------------------------------------------------------------------
1 | _site
2 |
--------------------------------------------------------------------------------
/spec/test-sité/_config.dev.toml:
--------------------------------------------------------------------------------
1 | hello = "there"
2 |
--------------------------------------------------------------------------------
/spec/test-sité/_config.yml:
--------------------------------------------------------------------------------
1 | # Site settings
2 | title: jekyll-watch test site
3 | email: your-email@domain.com
4 | description: > # this means to ignore newlines until "baseurl:"
5 | Write an awesome description for your new site here. You can edit this
6 | line in _config.yml. It will appear in your document head meta (for
7 | Google search results) and in your feed.xml site description.
8 | baseurl: "" # the subpath of your site, e.g. /blog/
9 | url: "http://yourdomain.com" # the base hostname & protocol for your site
10 | twitter_username: jekyllrb
11 | github_username: jekyll
12 | exclude: [".gitignore"]
13 |
14 | # Build settings
15 | markdown: kramdown
16 |
--------------------------------------------------------------------------------
/spec/test-sité/_includes/footer.html:
--------------------------------------------------------------------------------
1 |
56 |
--------------------------------------------------------------------------------
/spec/test-sité/_includes/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/spec/test-sité/_includes/header.html:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/spec/test-sité/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include head.html %}
5 |
6 |
7 |
8 | {% include header.html %}
9 |
10 |
11 |
12 | {{ content }}
13 |
14 |
15 |
16 | {% include footer.html %}
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/spec/test-sité/_layouts/page.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
9 |
10 |
11 | {{ content }}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/test-sité/_layouts/post.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
10 |
11 |
12 | {{ content }}
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spec/test-sité/_posts/2014-08-08-welcome-to-jekyll.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Welcome to Jekyll!"
4 | date: 2014-08-08 18:00:36
5 | categories: jekyll update
6 | ---
7 | You’ll find this post in your `_posts` directory – edit it and re-build (or run with the `--watch` switch) to see your changes.
8 |
9 | To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.
10 |
11 | Jekyll also offers powerful support for code snippets:
12 |
13 | {% highlight ruby %}
14 | def print_hi(name)
15 | puts "Hi, #{name}"
16 | end
17 | print_hi('Tom')
18 | #=> prints 'Hi, Tom' to STDOUT.
19 | {% endhighlight %}
20 |
21 | Check out the [Jekyll docs][jekyll] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll’s dedicated Help repository][jekyll-help].
22 |
23 | [jekyll]: http://jekyllrb.com
24 | [jekyll-gh]: https://github.com/jekyll/jekyll
25 | [jekyll-help]: https://github.com/jekyll/jekyll-help
26 |
--------------------------------------------------------------------------------
/spec/test-sité/_posts/2014-08-08-歡迎使用jekyll.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "歡迎使用Jekyll"
4 | date: 2014-08-08 18:00:36
5 | categories: jekyll update
6 | ---
7 | 你會在你的`_posts`目錄中找到這篇文章 - 編輯它並重新構建(或使用`--watch`開關運行)來查看你的更改。
8 |
9 | 要添加新帖子,只需在`_posts`目錄中添加一個文件,該文件遵循慣例`YYYY-MM-DD-name-of-post.ext`並包含必要的前置事項。 看看這篇文章的來源,了解它的工作原理。
10 |
11 | Jekyll還為代碼片段提供強大的支持:
12 |
13 | {% highlight ruby %}
14 | def print_hi(name)
15 | puts "Hi, #{name}"
16 | end
17 | print_hi('Tom')
18 | #=> prints 'Hi, Tom' to STDOUT.
19 | {% endhighlight %}
20 |
21 | 查看[Jekyll docs] [jekyll]了解更多關於如何充分利用Jekyll的信息。 在[Jekyll的GitHub repo] [jekyll-gh]提交所有錯誤/功能請求。 如果您有疑問,可以在[Jekyll的專用幫助存儲庫] [jekyll-help]上詢問。
22 |
23 | [jekyll]: http://jekyllrb.com
24 | [jekyll-gh]: https://github.com/jekyll/jekyll
25 | [jekyll-help]: https://github.com/jekyll/jekyll-help
26 |
--------------------------------------------------------------------------------
/spec/test-sité/_sass/_base.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Reset some basic elements
3 | */
4 | body, h1, h2, h3, h4, h5, h6,
5 | p, blockquote, pre, hr,
6 | dl, dd, ol, ul, figure {
7 | margin: 0;
8 | padding: 0;
9 | }
10 |
11 | /**
12 | * Basic styling
13 | */
14 | body {
15 | font-family: $base-font-family;
16 | font-size: $base-font-size;
17 | line-height: $base-line-height;
18 | font-weight: 300;
19 | color: $text-color;
20 | background-color: $background-color;
21 | }
22 |
23 |
24 |
25 | /**
26 | * Set `margin-bottom` to maintain vertycal rhythm
27 | */
28 | h1, h2, h3, h4, h5, h6,
29 | p, blockquote, pre,
30 | ul, ol, dl, figure,
31 | %vertical-rhythm {
32 | margin-bottom: $spacing-unit / 2;
33 | }
34 |
35 |
36 |
37 | /**
38 | * Images
39 | */
40 | img {
41 | max-width: 100%;
42 | vertical-align: middle;
43 | }
44 |
45 |
46 |
47 | /**
48 | * Figures
49 | */
50 | figure > img {
51 | display: block;
52 | }
53 |
54 | figcaption {
55 | font-size: $small-font-size;
56 | }
57 |
58 |
59 |
60 | /**
61 | * Lists
62 | */
63 | ul, ol {
64 | margin-left: $spacing-unit;
65 | }
66 |
67 | li {
68 | > ul,
69 | > ol {
70 | margin-bottom: 0;
71 | }
72 | }
73 |
74 |
75 |
76 | /**
77 | * Headings
78 | */
79 | h1, h2, h3, h4, h5, h6 {
80 | font-weight: 300;
81 | }
82 |
83 |
84 |
85 | /**
86 | * Links
87 | */
88 | a {
89 | color: $brand-color;
90 | text-decoration: none;
91 |
92 | &:visited {
93 | color: darken($brand-color, 15%);
94 | }
95 |
96 | &:hover {
97 | color: $text-color;
98 | text-decoration: underline;
99 | }
100 | }
101 |
102 |
103 |
104 | /**
105 | * Blockquotes
106 | */
107 | blockquote {
108 | color: $grey-color;
109 | border-left: 4px solid $grey-color-light;
110 | padding-left: $spacing-unit / 2;
111 | font-size: 18px;
112 | letter-spacing: -1px;
113 | font-style: italic;
114 |
115 | > :last-child {
116 | margin-bottom: 0;
117 | }
118 | }
119 |
120 |
121 |
122 | /**
123 | * Code formatting
124 | */
125 | pre,
126 | code {
127 | font-size: 15px;
128 | border: 1px solid $grey-color-light;
129 | border-radius: 3px;
130 | background-color: #eef;
131 | }
132 |
133 | code {
134 | padding: 1px 5px;
135 | }
136 |
137 | pre {
138 | padding: 8px 12px;
139 | overflow-x: scroll;
140 |
141 | > code {
142 | border: 0;
143 | padding-right: 0;
144 | padding-left: 0;
145 | }
146 | }
147 |
148 |
149 |
150 | /**
151 | * Wrapper
152 | */
153 | .wrapper {
154 | max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
155 | max-width: calc(800px - (#{$spacing-unit} * 2));
156 | margin-right: auto;
157 | margin-left: auto;
158 | padding-right: $spacing-unit;
159 | padding-left: $spacing-unit;
160 | @extend %clearfix;
161 |
162 | @include media-query($on-laptop) {
163 | max-width: -webkit-calc(800px - (#{$spacing-unit}));
164 | max-width: calc(800px - (#{$spacing-unit}));
165 | padding-right: $spacing-unit / 2;
166 | padding-left: $spacing-unit / 2;
167 | }
168 | }
169 |
170 |
171 |
172 | /**
173 | * Clearfix
174 | */
175 | %clearfix {
176 |
177 | &:after {
178 | content: "";
179 | display: table;
180 | clear: both;
181 | }
182 | }
183 |
184 |
185 |
186 | /**
187 | * Icons
188 | */
189 | .icon {
190 |
191 | > svg {
192 | display: inline-block;
193 | width: 16px;
194 | height: 16px;
195 | vertical-align: middle;
196 |
197 | path {
198 | fill: $grey-color;
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/spec/test-sité/_sass/_layout.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Site header
3 | */
4 | .site-header {
5 | border-top: 5px solid $grey-color-dark;
6 | border-bottom: 1px solid $grey-color-light;
7 | min-height: 56px;
8 |
9 | // Positioning context for the mobile navigation icon
10 | position: relative;
11 | }
12 |
13 | .site-title {
14 | font-size: 26px;
15 | line-height: 56px;
16 | letter-spacing: -1px;
17 | margin-bottom: 0;
18 | float: left;
19 |
20 | &,
21 | &:visited {
22 | color: $grey-color-dark;
23 | }
24 | }
25 |
26 | .site-nav {
27 | float: right;
28 | line-height: 56px;
29 |
30 | .menu-icon {
31 | display: none;
32 | }
33 |
34 | .page-link {
35 | color: $text-color;
36 | line-height: $base-line-height;
37 |
38 | // Gaps between nav items, but not on the first one
39 | &:not(:first-child) {
40 | margin-left: 20px;
41 | }
42 | }
43 |
44 | @include media-query($on-palm) {
45 | position: absolute;
46 | top: 9px;
47 | right: 30px;
48 | background-color: $background-color;
49 | border: 1px solid $grey-color-light;
50 | border-radius: 5px;
51 | text-align: right;
52 |
53 | .menu-icon {
54 | display: block;
55 | float: right;
56 | width: 36px;
57 | height: 26px;
58 | line-height: 0;
59 | padding-top: 10px;
60 | text-align: center;
61 |
62 | > svg {
63 | width: 18px;
64 | height: 15px;
65 |
66 | path {
67 | fill: $grey-color-dark;
68 | }
69 | }
70 | }
71 |
72 | .trigger {
73 | clear: both;
74 | display: none;
75 | }
76 |
77 | &:hover .trigger {
78 | display: block;
79 | padding-bottom: 5px;
80 | }
81 |
82 | .page-link {
83 | display: block;
84 | padding: 5px 10px;
85 | }
86 | }
87 | }
88 |
89 |
90 |
91 | /**
92 | * Site footer
93 | */
94 | .site-footer {
95 | border-top: 1px solid $grey-color-light;
96 | padding: $spacing-unit 0;
97 | }
98 |
99 | .footer-heading {
100 | font-size: 18px;
101 | margin-bottom: $spacing-unit / 2;
102 | }
103 |
104 | .contact-list,
105 | .social-media-list {
106 | list-style: none;
107 | margin-left: 0;
108 | }
109 |
110 | .footer-col-wrapper {
111 | font-size: 15px;
112 | color: $grey-color;
113 | margin-left: -$spacing-unit / 2;
114 | @extend %clearfix;
115 | }
116 |
117 | .footer-col {
118 | float: left;
119 | margin-bottom: $spacing-unit / 2;
120 | padding-left: $spacing-unit / 2;
121 | }
122 |
123 | .footer-col-1 {
124 | width: -webkit-calc(35% - (#{$spacing-unit} / 2));
125 | width: calc(35% - (#{$spacing-unit} / 2));
126 | }
127 |
128 | .footer-col-2 {
129 | width: -webkit-calc(20% - (#{$spacing-unit} / 2));
130 | width: calc(20% - (#{$spacing-unit} / 2));
131 | }
132 |
133 | .footer-col-3 {
134 | width: -webkit-calc(45% - (#{$spacing-unit} / 2));
135 | width: calc(45% - (#{$spacing-unit} / 2));
136 | }
137 |
138 | @include media-query($on-laptop) {
139 | .footer-col-1,
140 | .footer-col-2 {
141 | width: -webkit-calc(50% - (#{$spacing-unit} / 2));
142 | width: calc(50% - (#{$spacing-unit} / 2));
143 | }
144 |
145 | .footer-col-3 {
146 | width: -webkit-calc(100% - (#{$spacing-unit} / 2));
147 | width: calc(100% - (#{$spacing-unit} / 2));
148 | }
149 | }
150 |
151 | @include media-query($on-palm) {
152 | .footer-col {
153 | float: none;
154 | width: -webkit-calc(100% - (#{$spacing-unit} / 2));
155 | width: calc(100% - (#{$spacing-unit} / 2));
156 | }
157 | }
158 |
159 |
160 |
161 | /**
162 | * Page content
163 | */
164 | .page-content {
165 | padding: $spacing-unit 0;
166 | }
167 |
168 | .page-heading {
169 | font-size: 20px;
170 | }
171 |
172 | .post-list {
173 | margin-left: 0;
174 | list-style: none;
175 |
176 | > li {
177 | margin-bottom: $spacing-unit;
178 | }
179 | }
180 |
181 | .post-meta {
182 | font-size: $small-font-size;
183 | color: $grey-color;
184 | }
185 |
186 | .post-link {
187 | display: block;
188 | font-size: 24px;
189 | }
190 |
191 |
192 |
193 | /**
194 | * Posts
195 | */
196 | .post-header {
197 | margin-bottom: $spacing-unit;
198 | }
199 |
200 | .post-title {
201 | font-size: 42px;
202 | letter-spacing: -1px;
203 | line-height: 1;
204 |
205 | @include media-query($on-laptop) {
206 | font-size: 36px;
207 | }
208 | }
209 |
210 | .post-content {
211 | margin-bottom: $spacing-unit;
212 |
213 | h2 {
214 | font-size: 32px;
215 |
216 | @include media-query($on-laptop) {
217 | font-size: 28px;
218 | }
219 | }
220 |
221 | h3 {
222 | font-size: 26px;
223 |
224 | @include media-query($on-laptop) {
225 | font-size: 22px;
226 | }
227 | }
228 |
229 | h4 {
230 | font-size: 20px;
231 |
232 | @include media-query($on-laptop) {
233 | font-size: 18px;
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/spec/test-sité/_sass/_syntax-highlighting.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Syntax highlighting styles
3 | */
4 | .highlight {
5 | background: #fff;
6 | @extend %vertical-rhythm;
7 |
8 | .c { color: #998; font-style: italic } // Comment
9 | .err { color: #a61717; background-color: #e3d2d2 } // Error
10 | .k { font-weight: bold } // Keyword
11 | .o { font-weight: bold } // Operator
12 | .cm { color: #998; font-style: italic } // Comment.Multiline
13 | .cp { color: #999; font-weight: bold } // Comment.Preproc
14 | .c1 { color: #998; font-style: italic } // Comment.Single
15 | .cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
16 | .gd { color: #000; background-color: #fdd } // Generic.Deleted
17 | .gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
18 | .ge { font-style: italic } // Generic.Emph
19 | .gr { color: #a00 } // Generic.Error
20 | .gh { color: #999 } // Generic.Heading
21 | .gi { color: #000; background-color: #dfd } // Generic.Inserted
22 | .gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
23 | .go { color: #888 } // Generic.Output
24 | .gp { color: #555 } // Generic.Prompt
25 | .gs { font-weight: bold } // Generic.Strong
26 | .gu { color: #aaa } // Generic.Subheading
27 | .gt { color: #a00 } // Generic.Traceback
28 | .kc { font-weight: bold } // Keyword.Constant
29 | .kd { font-weight: bold } // Keyword.Declaration
30 | .kp { font-weight: bold } // Keyword.Pseudo
31 | .kr { font-weight: bold } // Keyword.Reserved
32 | .kt { color: #458; font-weight: bold } // Keyword.Type
33 | .m { color: #099 } // Literal.Number
34 | .s { color: #d14 } // Literal.String
35 | .na { color: #008080 } // Name.Attribute
36 | .nb { color: #0086B3 } // Name.Builtin
37 | .nc { color: #458; font-weight: bold } // Name.Class
38 | .no { color: #008080 } // Name.Constant
39 | .ni { color: #800080 } // Name.Entity
40 | .ne { color: #900; font-weight: bold } // Name.Exception
41 | .nf { color: #900; font-weight: bold } // Name.Function
42 | .nn { color: #555 } // Name.Namespace
43 | .nt { color: #000080 } // Name.Tag
44 | .nv { color: #008080 } // Name.Variable
45 | .ow { font-weight: bold } // Operator.Word
46 | .w { color: #bbb } // Text.Whitespace
47 | .mf { color: #099 } // Literal.Number.Float
48 | .mh { color: #099 } // Literal.Number.Hex
49 | .mi { color: #099 } // Literal.Number.Integer
50 | .mo { color: #099 } // Literal.Number.Oct
51 | .sb { color: #d14 } // Literal.String.Backtick
52 | .sc { color: #d14 } // Literal.String.Char
53 | .sd { color: #d14 } // Literal.String.Doc
54 | .s2 { color: #d14 } // Literal.String.Double
55 | .se { color: #d14 } // Literal.String.Escape
56 | .sh { color: #d14 } // Literal.String.Heredoc
57 | .si { color: #d14 } // Literal.String.Interpol
58 | .sx { color: #d14 } // Literal.String.Other
59 | .sr { color: #009926 } // Literal.String.Regex
60 | .s1 { color: #d14 } // Literal.String.Single
61 | .ss { color: #990073 } // Literal.String.Symbol
62 | .bp { color: #999 } // Name.Builtin.Pseudo
63 | .vc { color: #008080 } // Name.Variable.Class
64 | .vg { color: #008080 } // Name.Variable.Global
65 | .vi { color: #008080 } // Name.Variable.Instance
66 | .il { color: #099 } // Literal.Number.Integer.Long
67 | }
68 |
--------------------------------------------------------------------------------
/spec/test-sité/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: About
4 | permalink: /about/
5 | ---
6 |
7 | This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](http://jekyllrb.com/)
8 |
9 | You can find the source code for the Jekyll new theme at: [github.com/jglovier/jekyll-new](https://github.com/jglovier/jekyll-new)
10 |
11 | You can find the source code for Jekyll at [github.com/jekyll/jekyll](https://github.com/jekyll/jekyll)
12 |
--------------------------------------------------------------------------------
/spec/test-sité/css/main.scss:
--------------------------------------------------------------------------------
1 | ---
2 | # Only the main Sass file needs front matter (the dashes are enough)
3 | layout: null
4 | ---
5 | @charset "utf-8";
6 |
7 |
8 |
9 | // Our variables
10 | $base-font-family: Helvetica, Arial, sans-serif;
11 | $base-font-size: 16px;
12 | $small-font-size: $base-font-size * 0.875;
13 | $base-line-height: 1.5;
14 |
15 | $spacing-unit: 30px;
16 |
17 | $text-color: #111;
18 | $background-color: #fdfdfd;
19 | $brand-color: #2a7ae2;
20 |
21 | $grey-color: #828282;
22 | $grey-color-light: lighten($grey-color, 40%);
23 | $grey-color-dark: darken($grey-color, 25%);
24 |
25 | $on-palm: 600px;
26 | $on-laptop: 800px;
27 |
28 |
29 |
30 | // Using media queries with like this:
31 | // @include media-query($palm) {
32 | // .wrapper {
33 | // padding-right: $spacing-unit / 2;
34 | // padding-left: $spacing-unit / 2;
35 | // }
36 | // }
37 | @mixin media-query($device) {
38 | @media screen and (max-width: $device) {
39 | @content;
40 | }
41 | }
42 |
43 |
44 |
45 | // Import partials from `sass_dir` (defaults to `_sass`)
46 | @import
47 | "base",
48 | "layout",
49 | "syntax-highlighting"
50 | ;
51 |
--------------------------------------------------------------------------------
/spec/test-sité/feed.xml:
--------------------------------------------------------------------------------
1 | ---
2 | layout: null
3 | ---
4 |
5 |
6 |
7 | {{ site.title | xml_escape }}
8 | {{ site.description | xml_escape }}
9 | {{ site.url }}{{ site.baseurl }}/
10 |
11 | {{ site.time | date_to_rfc822 }}
12 | {{ site.time | date_to_rfc822 }}
13 | Jekyll v{{ jekyll.version }}
14 | {% for post in site.posts limit:10 %}
15 | -
16 | {{ post.title | xml_escape }}
17 | {{ post.content | xml_escape }}
18 | {{ post.date | date_to_rfc822 }}
19 | {{ post.url | prepend: site.baseurl | prepend: site.url }}
20 | {{ post.url | prepend: site.baseurl | prepend: site.url }}
21 | {% for tag in post.tags %}
22 | {{ tag | xml_escape }}
23 | {% endfor %}
24 | {% for cat in post.categories %}
25 | {{ cat | xml_escape }}
26 | {% endfor %}
27 |
28 | {% endfor %}
29 |
30 |
31 |
--------------------------------------------------------------------------------
/spec/test-sité/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
7 |
Posts
8 |
9 |
10 | {% for post in site.posts %}
11 | -
12 | {{ post.date | date: "%b %-d, %Y" }}
13 |
14 |
17 |
18 | {% endfor %}
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/spec/watcher_spec.rb:
--------------------------------------------------------------------------------
1 | require "spec_helper"
2 |
3 | describe(Jekyll::Watcher) do
4 | let(:base_opts) do
5 | {
6 | "source" => source_dir,
7 | "destination" => dest_dir,
8 | }
9 | end
10 |
11 | let(:options) { base_opts }
12 | let(:site) { instance_double(Jekyll::Site) }
13 | let(:default_ignored) { [%r!^_config\.yml!, %r!^_site/!, %r!^\.jekyll\-metadata!] }
14 | subject { described_class }
15 | before(:each) do
16 | FileUtils.mkdir(options["destination"]) if options["destination"]
17 | end
18 |
19 | after(:each) do
20 | FileUtils.rm_rf(options["destination"]) if options["destination"]
21 | end
22 |
23 | describe "#watch" do
24 | let(:listener) { instance_double(Listen::Listener) }
25 |
26 | let(:opts) do
27 | { :ignore => default_ignored, :force_polling => options["force_polling"] }
28 | end
29 |
30 | before do
31 | allow(Listen).to receive(:to).with(options["source"], opts).and_return(listener)
32 |
33 | allow(listener).to receive(:start)
34 |
35 | allow(Jekyll::Site).to receive(:new).with(options).and_return(site)
36 | allow(Jekyll.logger).to receive(:info)
37 |
38 | allow(subject).to receive(:sleep_forever)
39 |
40 | subject.watch(options)
41 | end
42 |
43 | it "starts the listener" do
44 | expect(listener).to have_received(:start)
45 | end
46 |
47 | it "sleeps" do
48 | expect(subject).to have_received(:sleep_forever)
49 | end
50 |
51 | it "ignores the config and site by default" do
52 | expect(Listen)
53 | .to have_received(:to)
54 | .with(anything, hash_including(:ignore => default_ignored))
55 | end
56 |
57 | it "defaults to no force_polling" do
58 | expect(Listen)
59 | .to have_received(:to)
60 | .with(anything, hash_including(:force_polling => nil))
61 | end
62 |
63 | context "with force_polling turned on" do
64 | let(:options) { base_opts.merge("force_polling" => true) }
65 |
66 | it "respects the custom value of force_polling" do
67 | expect(Listen)
68 | .to have_received(:to)
69 | .with(anything, hash_including(:force_polling => true))
70 | end
71 | end
72 | end
73 |
74 | describe "#watch using site instance" do
75 | let(:listener) { instance_double(Listen::Listener) }
76 |
77 | let(:opts) { { :ignore => default_ignored, :force_polling => nil } }
78 |
79 | before do
80 | allow(Listen)
81 | .to receive(:to)
82 | .with(options["source"], opts)
83 | .and_return(listener)
84 |
85 | allow(listener).to receive(:start)
86 |
87 | allow(Jekyll.logger).to receive(:info)
88 |
89 | allow(subject).to receive(:sleep_forever)
90 |
91 | subject.watch(options, site)
92 | end
93 |
94 | it "does not create a new site instance" do
95 | expect(listener).to have_received(:start)
96 | end
97 | end
98 |
99 | context "#listen_ignore_paths" do
100 | let(:ignored) { subject.send(:listen_ignore_paths, options) }
101 | let(:metadata_path) { Jekyll.sanitized_path(options["source"], ".jekyll-metadata") }
102 |
103 | before(:each) { FileUtils.touch(metadata_path) }
104 | after(:each) { FileUtils.rm(metadata_path) }
105 |
106 | it "ignores config.yml, .jekyll-metadata, and _site by default" do
107 | expect(ignored).to eql(default_ignored)
108 | expect(ignore_path?(ignored, "_site/foo.html")).to eql(true)
109 | expect(ignore_path?(ignored, "_sitemapper/foo.html")).to eql(false)
110 | expect(ignore_path?(ignored, "bar/_site/foo.html")).to eql(false)
111 | expect(ignore_path?(ignored, "bar/_site-mapper.html")).to eql(false)
112 | end
113 |
114 | context "with something excluded" do
115 | let(:excluded) { ["README.md", "LICENSE"] }
116 | let(:excluded_absolute) do
117 | excluded.map { |p| Jekyll.sanitized_path(options["source"], p) }
118 | end
119 | let(:options) { base_opts.merge("exclude" => excluded) }
120 | before(:each) { FileUtils.touch(excluded_absolute) }
121 | after(:each) { FileUtils.rm(excluded_absolute) }
122 |
123 | it "ignores the excluded files" do
124 | expect(ignore_path?(ignored, "README.md")).to eql(true)
125 | expect(ignore_path?(ignored, "LICENSE")).to eql(true)
126 | end
127 | end
128 |
129 | context "with a custom destination" do
130 | let(:default_ignored) { [%r!^_config\.yml!, %r!^_dest/!, %r!^\.jekyll\-metadata!] }
131 |
132 | context "when source is absolute" do
133 | context "when destination is absolute" do
134 | let(:options) { base_opts.merge("destination" => source_dir("_dest")) }
135 | it "ignores the destination" do
136 | expect(ignored).to eql(default_ignored)
137 | expect(ignore_path?(ignored, "_dest/foo.html")).to eql(true)
138 | expect(ignore_path?(ignored, "_destination/foo.html")).to eql(false)
139 | expect(ignore_path?(ignored, "bar/_dest/foo.html")).to eql(false)
140 | expect(ignore_path?(ignored, "bar/_dest-nation.html")).to eql(false)
141 | end
142 | end
143 |
144 | context "when destination is relative" do
145 | let(:options) { base_opts.merge("destination" => "spec/test-sité/_dest") }
146 | it "ignores the destination" do
147 | expect(ignored).to eql(default_ignored)
148 | expect(ignore_path?(ignored, "_dest/foo.html")).to eql(true)
149 | expect(ignore_path?(ignored, "_destination/foo.html")).to eql(false)
150 | expect(ignore_path?(ignored, "bar/_dest/foo.html")).to eql(false)
151 | expect(ignore_path?(ignored, "bar/_dest-nation.html")).to eql(false)
152 | end
153 | end
154 | end
155 |
156 | context "when source is relative" do
157 | let(:base_opts) do
158 | { "source" => Pathname
159 | .new(source_dir)
160 | .relative_path_from(Pathname.new(".")
161 | .expand_path).to_s, }
162 | end
163 |
164 | context "when destination is absolute" do
165 | let(:options) { base_opts.merge("destination" => source_dir("_dest")) }
166 | it "ignores the destination" do
167 | expect(ignored).to eql(default_ignored)
168 | expect(ignore_path?(ignored, "_dest/foo.html")).to eql(true)
169 | expect(ignore_path?(ignored, "_destination/foo.html")).to eql(false)
170 | expect(ignore_path?(ignored, "bar/_dest/foo.html")).to eql(false)
171 | expect(ignore_path?(ignored, "bar/_dest-nation.html")).to eql(false)
172 | end
173 | end
174 |
175 | context "when destination is relative" do
176 | let(:options) { base_opts.merge("destination" => "spec/test-sité/_dest") }
177 | it "ignores the destination" do
178 | expect(ignored).to eql(default_ignored)
179 | expect(ignore_path?(ignored, "_dest/foo.html")).to eql(true)
180 | expect(ignore_path?(ignored, "_destination/foo.html")).to eql(false)
181 | expect(ignore_path?(ignored, "bar/_dest/foo.html")).to eql(false)
182 | expect(ignore_path?(ignored, "bar/_dest-nation.html")).to eql(false)
183 | end
184 | end
185 | end
186 | end
187 | end
188 | end
189 |
--------------------------------------------------------------------------------