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
@@ -5,6 +5,128 @@ require "webrick"
5
5
  module Jekyll
6
6
  module Commands
7
7
  class Serve
8
+ # This class is used to determine if the Servlet should modify a served file
9
+ # to insert the LiveReload script tags
10
+ class SkipAnalyzer
11
+ BAD_USER_AGENTS = [%r!MSIE!].freeze
12
+
13
+ def self.skip_processing?(request, response, options)
14
+ new(request, response, options).skip_processing?
15
+ end
16
+
17
+ def initialize(request, response, options)
18
+ @options = options
19
+ @request = request
20
+ @response = response
21
+ end
22
+
23
+ def skip_processing?
24
+ !html? || chunked? || inline? || bad_browser?
25
+ end
26
+
27
+ def chunked?
28
+ @response["Transfer-Encoding"] == "chunked"
29
+ end
30
+
31
+ def inline?
32
+ @response["Content-Disposition"] =~ %r!^inline!
33
+ end
34
+
35
+ def bad_browser?
36
+ BAD_USER_AGENTS.any? { |pattern| @request["User-Agent"] =~ pattern }
37
+ end
38
+
39
+ def html?
40
+ @response["Content-Type"] =~ %r!text/html!
41
+ end
42
+ end
43
+
44
+ # This class inserts the LiveReload script tags into HTML as it is served
45
+ class BodyProcessor
46
+ HEAD_TAG_REGEX = %r!<head>|<head[^(er)][^<]*>!
47
+
48
+ attr_reader :content_length, :new_body, :livereload_added
49
+
50
+ def initialize(body, options)
51
+ @body = body
52
+ @options = options
53
+ @processed = false
54
+ end
55
+
56
+ def processed?
57
+ @processed
58
+ end
59
+
60
+ # rubocop:disable Metrics/MethodLength
61
+ def process!
62
+ @new_body = []
63
+ # @body will usually be a File object but Strings occur in rare cases
64
+ if @body.respond_to?(:each)
65
+ begin
66
+ @body.each { |line| @new_body << line.to_s }
67
+ ensure
68
+ @body.close
69
+ end
70
+ else
71
+ @new_body = @body.lines
72
+ end
73
+
74
+ @content_length = 0
75
+ @livereload_added = false
76
+
77
+ @new_body.each do |line|
78
+ if !@livereload_added && line["<head"]
79
+ line.gsub!(HEAD_TAG_REGEX) do |match|
80
+ %(#{match}#{template.result(binding)})
81
+ end
82
+
83
+ @livereload_added = true
84
+ end
85
+
86
+ @content_length += line.bytesize
87
+ @processed = true
88
+ end
89
+ @new_body = @new_body.join
90
+ end
91
+
92
+ def template
93
+ # Unclear what "snipver" does. Doc at
94
+ # https://github.com/livereload/livereload-js states that the recommended
95
+ # setting is 1.
96
+
97
+ # Complicated JavaScript to ensure that livereload.js is loaded from the
98
+ # same origin as the page. Mostly useful for dealing with the browser's
99
+ # distinction between 'localhost' and 127.0.0.1
100
+ template = <<-TEMPLATE
101
+ <script>
102
+ document.write(
103
+ '<script src="http://' +
104
+ (location.host || 'localhost').split(':')[0] +
105
+ ':<%=@options["livereload_port"] %>/livereload.js?snipver=1<%= livereload_args %>"' +
106
+ '></' +
107
+ 'script>');
108
+ </script>
109
+ TEMPLATE
110
+ ERB.new(Jekyll::Utils.strip_heredoc(template))
111
+ end
112
+
113
+ def livereload_args
114
+ # XHTML standard requires ampersands to be encoded as entities when in
115
+ # attributes. See http://stackoverflow.com/a/2190292
116
+ src = ""
117
+ if @options["livereload_min_delay"]
118
+ src += "&amp;mindelay=#{@options["livereload_min_delay"]}"
119
+ end
120
+ if @options["livereload_max_delay"]
121
+ src += "&amp;maxdelay=#{@options["livereload_max_delay"]}"
122
+ end
123
+ if @options["livereload_port"]
124
+ src += "&amp;port=#{@options["livereload_port"]}"
125
+ end
126
+ src
127
+ end
128
+ end
129
+
8
130
  class Servlet < WEBrick::HTTPServlet::FileHandler
9
131
  DEFAULTS = {
10
132
  "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
@@ -18,18 +140,37 @@ module Jekyll
18
140
  super
19
141
  end
20
142
 
143
+ def search_index_file(req, res)
144
+ super || search_file(req, res, ".html")
145
+ end
146
+
21
147
  # Add the ability to tap file.html the same way that Nginx does on our
22
148
  # Docker images (or on GitHub Pages.) The difference is that we might end
23
149
  # up with a different preference on which comes first.
24
150
 
25
151
  def search_file(req, res, basename)
26
152
  # /file.* > /file/index.html > /file.html
27
- super || super(req, res, ".html") || super(req, res, "#{basename}.html")
153
+ super || super(req, res, "#{basename}.html")
28
154
  end
29
155
 
30
156
  # rubocop:disable Naming/MethodName
31
157
  def do_GET(req, res)
32
158
  rtn = super
159
+
160
+ if @jekyll_opts["livereload"]
161
+ return rtn if SkipAnalyzer.skip_processing?(req, res, @jekyll_opts)
162
+
163
+ processor = BodyProcessor.new(res.body, @jekyll_opts)
164
+ processor.process!
165
+ res.body = processor.new_body
166
+ res.content_length = processor.content_length.to_s
167
+
168
+ if processor.livereload_added
169
+ # Add a header to indicate that the page content has been modified
170
+ res["X-Rack-LiveReload"] = "1"
171
+ end
172
+ end
173
+
33
174
  validate_and_ensure_charset(req, res)
34
175
  res.header.merge!(@headers)
35
176
  rtn
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "http/parser"
4
+
5
+ module Jekyll
6
+ module Commands
7
+ class Serve
8
+ # The LiveReload protocol requires the server to serve livereload.js over HTTP
9
+ # despite the fact that the protocol itself uses WebSockets. This custom connection
10
+ # class addresses the dual protocols that the server needs to understand.
11
+ class HttpAwareConnection < EventMachine::WebSocket::Connection
12
+ attr_reader :reload_body, :reload_size
13
+
14
+ def initialize(_opts)
15
+ # If EventMachine SSL support on Windows ever gets better, the code below will
16
+ # set up the reactor to handle SSL
17
+ #
18
+ # @ssl_enabled = opts["ssl_cert"] && opts["ssl_key"]
19
+ # if @ssl_enabled
20
+ # em_opts[:tls_options] = {
21
+ # :private_key_file => Jekyll.sanitized_path(opts["source"], opts["ssl_key"]),
22
+ # :cert_chain_file => Jekyll.sanitized_path(opts["source"], opts["ssl_cert"])
23
+ # }
24
+ # em_opts[:secure] = true
25
+ # end
26
+
27
+ # This is too noisy even for --verbose, but uncomment if you need it for
28
+ # a specific WebSockets issue. Adding ?LR-verbose=true onto the URL will
29
+ # enable logging on the client side.
30
+ # em_opts[:debug] = true
31
+
32
+ em_opts = {}
33
+ super(em_opts)
34
+
35
+ reload_file = File.join(Serve.singleton_class::LIVERELOAD_DIR, "livereload.js")
36
+
37
+ @reload_body = File.read(reload_file)
38
+ @reload_size = @reload_body.bytesize
39
+ end
40
+
41
+ # rubocop:disable Metrics/MethodLength
42
+ def dispatch(data)
43
+ parser = Http::Parser.new
44
+ parser << data
45
+
46
+ # WebSockets requests will have a Connection: Upgrade header
47
+ if parser.http_method != "GET" || parser.upgrade?
48
+ super
49
+ elsif parser.request_url =~ %r!^\/livereload.js!
50
+ headers = [
51
+ "HTTP/1.1 200 OK",
52
+ "Content-Type: application/javascript",
53
+ "Content-Length: #{reload_size}",
54
+ "",
55
+ "",
56
+ ].join("\r\n")
57
+ send_data(headers)
58
+
59
+ # stream_file_data would free us from keeping livereload.js in memory
60
+ # but JRuby blocks on that call and never returns
61
+ send_data(reload_body)
62
+ close_connection_after_writing
63
+ else
64
+ body = "This port only serves livereload.js over HTTP.\n"
65
+ headers = [
66
+ "HTTP/1.1 400 Bad Request",
67
+ "Content-Type: text/plain",
68
+ "Content-Length: #{body.bytesize}",
69
+ "",
70
+ "",
71
+ ].join("\r\n")
72
+ send_data(headers)
73
+ send_data(body)
74
+ close_connection_after_writing
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -8,6 +8,7 @@ module Jekyll
8
8
  # Where things are
9
9
  "source" => Dir.pwd,
10
10
  "destination" => File.join(Dir.pwd, "_site"),
11
+ "collections_dir" => "",
11
12
  "plugins_dir" => "_plugins",
12
13
  "layouts_dir" => "_layouts",
13
14
  "data_dir" => "_data",
@@ -79,6 +80,7 @@ module Jekyll
79
80
  "input" => "GFM",
80
81
  "hard_wrap" => false,
81
82
  "footnote_nr" => 1,
83
+ "show_warnings" => false,
82
84
  },
83
85
  }.map { |k, v| [k, v.freeze] }].freeze
84
86
 
@@ -95,7 +97,7 @@ module Jekyll
95
97
  # problems and backwards-compatibility.
96
98
  def from(user_config)
97
99
  Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
98
- .fix_common_issues.add_default_collections
100
+ .add_default_collections
99
101
  end
100
102
  end
101
103
 
@@ -132,8 +134,8 @@ module Jekyll
132
134
  def safe_load_file(filename)
133
135
  case File.extname(filename)
134
136
  when %r!\.toml!i
135
- Jekyll::External.require_with_graceful_fail("toml") unless defined?(TOML)
136
- TOML.load_file(filename)
137
+ Jekyll::External.require_with_graceful_fail("tomlrb") unless defined?(Tomlrb)
138
+ Tomlrb.load_file(filename)
137
139
  when %r!\.ya?ml!i
138
140
  SafeYAML.load_file(filename) || {}
139
141
  else
@@ -163,8 +165,7 @@ module Jekyll
163
165
  config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
164
166
  @default_config_file = true
165
167
  end
166
- config_files = [config_files] unless config_files.is_a? Array
167
- config_files
168
+ Array(config_files)
168
169
  end
169
170
 
170
171
  # Public: Read configuration and return merged Hash
@@ -209,7 +210,7 @@ module Jekyll
209
210
  warn err
210
211
  end
211
212
 
212
- configuration.fix_common_issues.backwards_compatibilize.add_default_collections
213
+ configuration.backwards_compatibilize.add_default_collections
213
214
  end
214
215
 
215
216
  # Public: Split a CSV string into an array containing its values
@@ -245,18 +246,9 @@ module Jekyll
245
246
  config
246
247
  end
247
248
 
249
+ # DEPRECATED.
248
250
  def fix_common_issues
249
- config = clone
250
-
251
- if config.key?("paginate") && (!config["paginate"].is_a?(Integer) ||
252
- config["paginate"] < 1)
253
-
254
- Jekyll.logger.warn "Config Warning:", "The `paginate` key must be a positive" \
255
- " integer or nil. It's currently set to '#{config["paginate"].inspect}'."
256
- config["paginate"] = nil
257
- end
258
-
259
- config
251
+ self
260
252
  end
261
253
 
262
254
  def add_default_collections
@@ -44,7 +44,7 @@ module Jekyll
44
44
  # are not in safe mode.)
45
45
 
46
46
  def valid_processors
47
- %W(rdiscount kramdown redcarpet) + third_party_processors
47
+ %w(rdiscount kramdown redcarpet) + third_party_processors
48
48
  end
49
49
 
50
50
  # Public: A list of processors that you provide via plugins.
@@ -1,5 +1,4 @@
1
1
  # Frozen-string-literal: true
2
- # Encoding: utf-8
3
2
 
4
3
  module Jekyll
5
4
  module Converters
@@ -38,7 +37,14 @@ module Jekyll
38
37
  end
39
38
 
40
39
  def convert(content)
41
- Kramdown::Document.new(content, @config).to_html
40
+ document = Kramdown::Document.new(content, @config)
41
+ html_output = document.to_html
42
+ if @config["show_warnings"]
43
+ document.warnings.each do |warning|
44
+ Jekyll.logger.warn "Kramdown warning:", warning
45
+ end
46
+ end
47
+ html_output
42
48
  end
43
49
 
44
50
  private
@@ -3,9 +3,14 @@
3
3
  class Kramdown::Parser::SmartyPants < Kramdown::Parser::Kramdown
4
4
  def initialize(source, options)
5
5
  super
6
- @block_parsers = [:block_html]
6
+ @block_parsers = [:block_html, :content]
7
7
  @span_parsers = [:smart_quotes, :html_entity, :typographic_syms, :span_html]
8
8
  end
9
+
10
+ def parse_content
11
+ add_text @src.scan(%r!\A.*\n!)
12
+ end
13
+ define_parser(:content, %r!\A!)
9
14
  end
10
15
 
11
16
  module Jekyll
@@ -29,7 +34,14 @@ module Jekyll
29
34
  end
30
35
 
31
36
  def convert(content)
32
- Kramdown::Document.new(content, @config).to_html.chomp
37
+ document = Kramdown::Document.new(content, @config)
38
+ html_output = document.to_html.chomp
39
+ if @config["show_warnings"]
40
+ document.warnings.each do |warning|
41
+ Jekyll.logger.warn "Kramdown warning:", warning.sub(%r!^Warning:\s+!, "")
42
+ end
43
+ end
44
+ html_output
33
45
  end
34
46
  end
35
47
  end
@@ -46,10 +46,10 @@ module Jekyll
46
46
  self.content = $POSTMATCH
47
47
  self.data = SafeYAML.load(Regexp.last_match(1))
48
48
  end
49
- rescue SyntaxError => e
49
+ rescue Psych::SyntaxError => e
50
50
  Jekyll.logger.warn "YAML Exception reading #{filename}: #{e.message}"
51
51
  raise e if self.site.config["strict_front_matter"]
52
- rescue => e
52
+ rescue StandardError => e
53
53
  Jekyll.logger.warn "Error reading file #{filename}: #{e.message}"
54
54
  raise e if self.site.config["strict_front_matter"]
55
55
  end
@@ -172,9 +172,10 @@ module Jekyll
172
172
 
173
173
  # Determine whether the file should be placed into layouts.
174
174
  #
175
- # Returns false if the document is an asset file.
175
+ # Returns false if the document is an asset file or if the front matter
176
+ # specifies `layout: none`
176
177
  def place_in_layout?
177
- !asset_file?
178
+ !(asset_file? || no_layout?)
178
179
  end
179
180
 
180
181
  # Checks if the layout specified in the document actually exists
@@ -244,8 +245,13 @@ module Jekyll
244
245
  end
245
246
 
246
247
  private
248
+
247
249
  def _renderer
248
250
  @_renderer ||= Jekyll::Renderer.new(site, self)
249
251
  end
252
+
253
+ def no_layout?
254
+ data["layout"] == "none"
255
+ end
250
256
  end
251
257
  end
@@ -266,7 +266,7 @@ module Jekyll
266
266
  merge_defaults
267
267
  read_content(opts)
268
268
  read_post_data
269
- rescue => e
269
+ rescue StandardError => e
270
270
  handle_read_error(e)
271
271
  end
272
272
  end
@@ -463,7 +463,7 @@ module Jekyll
463
463
 
464
464
  private
465
465
  def handle_read_error(error)
466
- if error.is_a? SyntaxError
466
+ if error.is_a? Psych::SyntaxError
467
467
  Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{error.message}"
468
468
  else
469
469
  Jekyll.logger.error "Error:", "could not read file #{path}: #{error.message}"
@@ -31,12 +31,9 @@ module Jekyll
31
31
 
32
32
  def filter(entries)
33
33
  entries.reject do |e|
34
- # Reject this entry if it is a symlink.
35
- next true if symlink?(e)
36
- # Do not reject this entry if it is included.
37
- next false if included?(e)
38
- # Reject this entry if it is special, a backup file, or excluded.
39
- special?(e) || backup?(e) || excluded?(e)
34
+ unless included?(e)
35
+ special?(e) || backup?(e) || excluded?(e) || symlink?(e)
36
+ end
40
37
  end
41
38
  end
42
39