jekyll 3.6.3 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -9
  3. data/LICENSE +1 -1
  4. data/README.markdown +1 -1
  5. data/lib/jekyll.rb +3 -0
  6. data/lib/jekyll/cleaner.rb +3 -1
  7. data/lib/jekyll/collection.rb +7 -3
  8. data/lib/jekyll/commands/clean.rb +1 -1
  9. data/lib/jekyll/commands/doctor.rb +3 -1
  10. data/lib/jekyll/commands/new.rb +5 -2
  11. data/lib/jekyll/commands/serve.rb +183 -41
  12. data/lib/jekyll/commands/serve/live_reload_reactor.rb +127 -0
  13. data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
  14. data/lib/jekyll/commands/serve/servlet.rb +142 -1
  15. data/lib/jekyll/commands/serve/websockets.rb +80 -0
  16. data/lib/jekyll/configuration.rb +9 -17
  17. data/lib/jekyll/converters/markdown.rb +1 -1
  18. data/lib/jekyll/converters/markdown/kramdown_parser.rb +8 -2
  19. data/lib/jekyll/converters/smartypants.rb +14 -2
  20. data/lib/jekyll/convertible.rb +10 -4
  21. data/lib/jekyll/document.rb +2 -2
  22. data/lib/jekyll/entry_filter.rb +3 -6
  23. data/lib/jekyll/filters/url_filters.rb +6 -1
  24. data/lib/jekyll/frontmatter_defaults.rb +18 -3
  25. data/lib/jekyll/hooks.rb +3 -0
  26. data/lib/jekyll/liquid_renderer.rb +8 -6
  27. data/lib/jekyll/page.rb +1 -1
  28. data/lib/jekyll/page_without_a_file.rb +18 -0
  29. data/lib/jekyll/regenerator.rb +1 -1
  30. data/lib/jekyll/renderer.rb +2 -2
  31. data/lib/jekyll/site.rb +1 -3
  32. data/lib/jekyll/tags/highlight.rb +2 -2
  33. data/lib/jekyll/tags/include.rb +1 -1
  34. data/lib/jekyll/tags/post_url.rb +1 -1
  35. data/lib/jekyll/theme.rb +3 -2
  36. data/lib/jekyll/theme_builder.rb +8 -8
  37. data/lib/jekyll/utils.rb +41 -22
  38. data/lib/jekyll/utils/ansi.rb +0 -2
  39. data/lib/jekyll/utils/internet.rb +39 -0
  40. data/lib/jekyll/utils/thread_event.rb +35 -0
  41. data/lib/jekyll/version.rb +1 -1
  42. metadata +41 -7
@@ -12,6 +12,7 @@ module Jekyll
12
12
  # Returns the absolute URL as a String.
13
13
  def absolute_url(input)
14
14
  return if input.nil?
15
+ input = input.url if input.respond_to?(:url)
15
16
  return input if Addressable::URI.parse(input.to_s).absolute?
16
17
  site = @context.registers[:site]
17
18
  return relative_url(input) if site.config["url"].nil?
@@ -20,13 +21,17 @@ module Jekyll
20
21
  ).normalize.to_s
21
22
  end
22
23
 
23
- # Produces a URL relative to the domain root based on site.baseurl.
24
+ # Produces a URL relative to the domain root based on site.baseurl
25
+ # unless it is already an absolute url with an authority (host).
24
26
  #
25
27
  # input - the URL to make relative to the domain root
26
28
  #
27
29
  # Returns a URL relative to the domain root as a String.
28
30
  def relative_url(input)
29
31
  return if input.nil?
32
+ input = input.url if input.respond_to?(:url)
33
+ return input if Addressable::URI.parse(input.to_s).absolute?
34
+
30
35
  parts = [sanitized_baseurl, input]
31
36
  Addressable::URI.parse(
32
37
  parts.compact.map { |part| ensure_leading_slash(part.to_s) }.join
@@ -100,12 +100,27 @@ module Jekyll
100
100
  def applies_path?(scope, path)
101
101
  return true if !scope.key?("path") || scope["path"].empty?
102
102
 
103
- scope_path = Pathname.new(scope["path"])
104
- Pathname.new(sanitize_path(path)).ascend do |ascended_path|
105
- if ascended_path.to_s == scope_path.to_s
103
+ sanitized_path = Pathname.new(sanitize_path(path))
104
+
105
+ site_path = Pathname.new(@site.source)
106
+ rel_scope_path = Pathname.new(scope["path"])
107
+ abs_scope_path = File.join(@site.source, rel_scope_path)
108
+ Dir.glob(abs_scope_path).each do |scope_path|
109
+ scope_path = Pathname.new(scope_path).relative_path_from site_path
110
+ return true if path_is_subpath?(sanitized_path, scope_path)
111
+ end
112
+
113
+ path_is_subpath?(sanitized_path, rel_scope_path)
114
+ end
115
+
116
+ def path_is_subpath?(path, parent_path)
117
+ path.ascend do |ascended_path|
118
+ if ascended_path.to_s == parent_path.to_s
106
119
  return true
107
120
  end
108
121
  end
122
+
123
+ false
109
124
  end
110
125
 
111
126
  # Determines whether the scope applies to type.
@@ -39,6 +39,9 @@ module Jekyll
39
39
  :post_render => [],
40
40
  :post_write => [],
41
41
  },
42
+ :clean => {
43
+ :on_obsolete => [],
44
+ },
42
45
  }
43
46
 
44
47
  # map of all hooks and their priorities
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "jekyll/liquid_renderer/file"
4
- require "jekyll/liquid_renderer/table"
3
+ require_relative "liquid_renderer/file"
4
+ require_relative "liquid_renderer/table"
5
5
 
6
6
  module Jekyll
7
7
  class LiquidRenderer
@@ -22,19 +22,16 @@ module Jekyll
22
22
  )
23
23
 
24
24
  LiquidRenderer::File.new(self, filename).tap do
25
- @stats[filename] ||= {}
26
- @stats[filename][:count] ||= 0
25
+ @stats[filename] ||= new_profile_hash
27
26
  @stats[filename][:count] += 1
28
27
  end
29
28
  end
30
29
 
31
30
  def increment_bytes(filename, bytes)
32
- @stats[filename][:bytes] ||= 0
33
31
  @stats[filename][:bytes] += bytes
34
32
  end
35
33
 
36
34
  def increment_time(filename, time)
37
- @stats[filename][:time] ||= 0.0
38
35
  @stats[filename][:time] += time
39
36
  end
40
37
 
@@ -45,5 +42,10 @@ module Jekyll
45
42
  def self.format_error(e, path)
46
43
  "#{e.message} in #{path}"
47
44
  end
45
+
46
+ private
47
+ def new_profile_hash
48
+ Hash.new { |hash, key| hash[key] = 0 }
49
+ end
48
50
  end
49
51
  end
@@ -163,7 +163,7 @@ module Jekyll
163
163
 
164
164
  # Returns the object as a debug String.
165
165
  def inspect
166
- "#<Jekyll:Page @name=#{name.inspect}>"
166
+ "#<Jekyll::Page @name=#{name.inspect}>"
167
167
  end
168
168
 
169
169
  # Returns the Boolean of whether this Page is HTML or not.
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ # A Jekyll::Page subclass to handle processing files without reading it to
5
+ # determine the page-data and page-content based on Front Matter delimiters.
6
+ #
7
+ # The class instance is basically just a bare-bones entity with just
8
+ # attributes "dir", "name", "path", "url" defined on it.
9
+ class PageWithoutAFile < Page
10
+ def read_yaml(*)
11
+ @data ||= {}
12
+ end
13
+
14
+ def inspect
15
+ "#<Jekyll:PageWithoutAFile @name=#{name.inspect}>"
16
+ end
17
+ end
18
+ end
@@ -128,7 +128,7 @@ module Jekyll
128
128
  #
129
129
  # Returns the String path of the file.
130
130
  def metadata_file
131
- site.in_source_dir(".jekyll-metadata")
131
+ @metadata_file ||= site.in_source_dir(".jekyll-metadata")
132
132
  end
133
133
 
134
134
  # Check if metadata has been disabled
@@ -96,7 +96,7 @@ module Jekyll
96
96
  converters.reduce(content) do |output, converter|
97
97
  begin
98
98
  converter.convert output
99
- rescue => e
99
+ rescue StandardError => e
100
100
  Jekyll.logger.error "Conversion error:",
101
101
  "#{converter.class} encountered an error while "\
102
102
  "converting '#{document.relative_path}':"
@@ -198,7 +198,7 @@ module Jekyll
198
198
  return unless document.write?
199
199
  site.regenerator.add_dependency(
200
200
  site.in_source_dir(document.path),
201
- site.in_source_dir(layout.path)
201
+ layout.path
202
202
  )
203
203
  end
204
204
 
@@ -122,10 +122,8 @@ module Jekyll
122
122
  dest_pathname = Pathname.new(dest)
123
123
  Pathname.new(source).ascend do |path|
124
124
  if path == dest_pathname
125
- raise(
126
- Errors::FatalException,
125
+ raise Errors::FatalException,
127
126
  "Destination directory cannot be or contain the Source directory."
128
- )
129
127
  end
130
128
  end
131
129
  end
@@ -86,7 +86,7 @@ MSG
86
86
  end
87
87
 
88
88
  def render_pygments(code, is_safe)
89
- Jekyll::External.require_with_graceful_fail("pygments")
89
+ Jekyll::External.require_with_graceful_fail("pygments") unless defined?(Pygments)
90
90
 
91
91
  highlighted_code = Pygments.highlight(
92
92
  code,
@@ -118,7 +118,7 @@ MSG
118
118
  :gutter_class => "gutter",
119
119
  :code_class => "code"
120
120
  )
121
- lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
121
+ lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
122
122
  formatter.format(lexer.lex(code))
123
123
  end
124
124
 
@@ -184,7 +184,7 @@ MSG
184
184
 
185
185
  def realpath_prefixed_with?(path, dir)
186
186
  File.exist?(path) && File.realpath(path).start_with?(dir)
187
- rescue
187
+ rescue StandardError
188
188
  false
189
189
  end
190
190
 
@@ -59,7 +59,7 @@ module Jekyll
59
59
  @orig_post = post.strip
60
60
  begin
61
61
  @post = PostComparer.new(@orig_post)
62
- rescue => e
62
+ rescue StandardError => e
63
63
  raise Jekyll::Errors::PostURLError, <<-MSG
64
64
  Could not parse name of post "#{@orig_post}" in tag 'post_url'.
65
65
 
@@ -16,7 +16,8 @@ module Jekyll
16
16
  # Otherwise, Jekyll.sanitized path with prepend the unresolved root
17
17
  @root ||= File.realpath(gemspec.full_gem_path)
18
18
  rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
19
- nil
19
+ raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
20
+ "or includes a symbolic link loop"
20
21
  end
21
22
 
22
23
  def includes_path
@@ -37,7 +38,7 @@ module Jekyll
37
38
 
38
39
  def configure_sass
39
40
  return unless sass_path
40
- require "sass"
41
+ Jekyll::External.require_with_graceful_fail "sass"
41
42
  Sass.load_paths << sass_path
42
43
  end
43
44
 
@@ -21,6 +21,14 @@ class Jekyll::ThemeBuilder
21
21
  initialize_git_repo
22
22
  end
23
23
 
24
+ def user_name
25
+ @user_name ||= `git config user.name`.chomp
26
+ end
27
+
28
+ def user_email
29
+ @user_email ||= `git config user.email`.chomp
30
+ end
31
+
24
32
  private
25
33
 
26
34
  def root
@@ -85,14 +93,6 @@ class Jekyll::ThemeBuilder
85
93
  write_file(".gitignore", template("gitignore"))
86
94
  end
87
95
 
88
- def user_name
89
- @user_name ||= `git config user.name`.chomp
90
- end
91
-
92
- def user_email
93
- @user_email ||= `git config user.email`.chomp
94
- end
95
-
96
96
  class ERBRenderer
97
97
  extend Forwardable
98
98
 
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Jekyll
@@ -6,12 +5,14 @@ module Jekyll
6
5
  extend self
7
6
  autoload :Ansi, "jekyll/utils/ansi"
8
7
  autoload :Exec, "jekyll/utils/exec"
8
+ autoload :Internet, "jekyll/utils/internet"
9
9
  autoload :Platforms, "jekyll/utils/platforms"
10
10
  autoload :Rouge, "jekyll/utils/rouge"
11
+ autoload :ThreadEvent, "jekyll/utils/thread_event"
11
12
  autoload :WinTZ, "jekyll/utils/win_tz"
12
13
 
13
14
  # Constants for use in #slugify
14
- SLUGIFY_MODES = %w(raw default pretty ascii).freeze
15
+ SLUGIFY_MODES = %w(raw default pretty ascii latin).freeze
15
16
  SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
16
17
  SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^[:alnum:]]+").freeze
17
18
  SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
@@ -169,6 +170,10 @@ module Jekyll
169
170
  # When mode is "ascii", some everything else except ASCII characters
170
171
  # a-z (lowercase), A-Z (uppercase) and 0-9 (numbers) are not replaced with hyphen.
171
172
  #
173
+ # When mode is "latin", the input string is first preprocessed so that
174
+ # any letters with accents are replaced with the plain letter. Afterwards,
175
+ # it follows the "default" mode of operation.
176
+ #
172
177
  # If cased is true, all uppercase letters in the result string are
173
178
  # replaced with their lowercase counterparts.
174
179
  #
@@ -183,7 +188,10 @@ module Jekyll
183
188
  # # => "The-_config.yml file"
184
189
  #
185
190
  # slugify("The _config.yml file", "ascii")
186
- # # => "the-config.yml-file"
191
+ # # => "the-config-yml-file"
192
+ #
193
+ # slugify("The _config.yml file", "latin")
194
+ # # => "the-config-yml-file"
187
195
  #
188
196
  # Returns the slugified string.
189
197
  def slugify(string, mode: nil, cased: false)
@@ -194,26 +202,10 @@ module Jekyll
194
202
  return cased ? string : string.downcase
195
203
  end
196
204
 
197
- # Replace each character sequence with a hyphen
198
- re =
199
- case mode
200
- when "raw"
201
- SLUGIFY_RAW_REGEXP
202
- when "default"
203
- SLUGIFY_DEFAULT_REGEXP
204
- when "pretty"
205
- # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
206
- # and is allowed in both extN and NTFS.
207
- SLUGIFY_PRETTY_REGEXP
208
- when "ascii"
209
- # For web servers not being able to handle Unicode, the safe
210
- # method is to ditch anything else but latin letters and numeric
211
- # digits.
212
- SLUGIFY_ASCII_REGEXP
213
- end
205
+ # Drop accent marks from latin characters. Everything else turns to ?
206
+ string = ::I18n.transliterate(string) if mode == "latin"
214
207
 
215
- # Strip according to the mode
216
- slug = string.gsub(re, "-")
208
+ slug = replace_character_sequence_with_hyphen(string, :mode => mode)
217
209
 
218
210
  # Remove leading/trailing hyphen
219
211
  slug.gsub!(%r!^\-|\-$!i, "")
@@ -336,5 +328,32 @@ module Jekyll
336
328
  target[key] = val.dup if val.frozen? && duplicable?(val)
337
329
  end
338
330
  end
331
+
332
+ # Replace each character sequence with a hyphen.
333
+ #
334
+ # See Utils#slugify for a description of the character sequence specified
335
+ # by each mode.
336
+ private
337
+ def replace_character_sequence_with_hyphen(string, mode: "default")
338
+ replaceable_char =
339
+ case mode
340
+ when "raw"
341
+ SLUGIFY_RAW_REGEXP
342
+ when "pretty"
343
+ # "._~!$&'()+,;=@" is human readable (not URI-escaped) in URL
344
+ # and is allowed in both extN and NTFS.
345
+ SLUGIFY_PRETTY_REGEXP
346
+ when "ascii"
347
+ # For web servers not being able to handle Unicode, the safe
348
+ # method is to ditch anything else but latin letters and numeric
349
+ # digits.
350
+ SLUGIFY_ASCII_REGEXP
351
+ else
352
+ SLUGIFY_DEFAULT_REGEXP
353
+ end
354
+
355
+ # Strip according to the mode
356
+ string.gsub(replaceable_char, "-")
357
+ end
339
358
  end
340
359
  end
@@ -1,6 +1,4 @@
1
1
  # Frozen-string-literal: true
2
- # Copyright: 2015 Jekyll - MIT License
3
- # Encoding: utf-8
4
2
 
5
3
  module Jekyll
6
4
  module Utils
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Utils
5
+ module Internet
6
+
7
+ # Public: Determine whether the present device has a connection to
8
+ # the Internet. This allows plugin writers which require the outside
9
+ # world to have a neat fallback mechanism for offline building.
10
+ #
11
+ # Example:
12
+ # if Internet.connected?
13
+ # Typhoeus.get("https://pages.github.com/versions.json")
14
+ # else
15
+ # Jekyll.logger.warn "Warning:", "Version check has been disabled."
16
+ # Jekyll.logger.warn "", "Connect to the Internet to enable it."
17
+ # nil
18
+ # end
19
+ #
20
+ # Returns true if a DNS call can successfully be made, or false if not.
21
+ module_function
22
+ def connected?
23
+ !dns("example.com").nil?
24
+ end
25
+
26
+ private
27
+ module_function
28
+ def dns(domain)
29
+ require "resolv"
30
+ Resolv::DNS.open do |resolver|
31
+ resolver.getaddress(domain)
32
+ end
33
+ rescue Resolv::ResolvError, Resolv::ResolvTimeout
34
+ nil
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+
5
+ module Jekyll
6
+ module Utils
7
+ # Based on the pattern and code from
8
+ # https://emptysqua.re/blog/an-event-synchronization-primitive-for-ruby/
9
+ class ThreadEvent
10
+ attr_reader :flag
11
+
12
+ def initialize
13
+ @lock = Mutex.new
14
+ @cond = ConditionVariable.new
15
+ @flag = false
16
+ end
17
+
18
+ def set
19
+ @lock.synchronize do
20
+ yield if block_given?
21
+ @flag = true
22
+ @cond.broadcast
23
+ end
24
+ end
25
+
26
+ def wait
27
+ @lock.synchronize do
28
+ unless @flag
29
+ @cond.wait(@lock)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end