jekyll 4.0.0.pre.alpha1 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +58 -17
  3. data/lib/jekyll.rb +5 -1
  4. data/lib/jekyll/cache.rb +71 -64
  5. data/lib/jekyll/cleaner.rb +5 -5
  6. data/lib/jekyll/collection.rb +6 -4
  7. data/lib/jekyll/command.rb +4 -2
  8. data/lib/jekyll/commands/new.rb +4 -4
  9. data/lib/jekyll/commands/serve.rb +9 -1
  10. data/lib/jekyll/commands/serve/servlet.rb +13 -14
  11. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  12. data/lib/jekyll/configuration.rb +40 -129
  13. data/lib/jekyll/converters/identity.rb +2 -2
  14. data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -9
  15. data/lib/jekyll/convertible.rb +21 -20
  16. data/lib/jekyll/document.rb +56 -31
  17. data/lib/jekyll/drops/document_drop.rb +12 -0
  18. data/lib/jekyll/drops/drop.rb +14 -8
  19. data/lib/jekyll/drops/site_drop.rb +11 -1
  20. data/lib/jekyll/drops/url_drop.rb +52 -1
  21. data/lib/jekyll/entry_filter.rb +38 -44
  22. data/lib/jekyll/excerpt.rb +3 -3
  23. data/lib/jekyll/filters.rb +140 -22
  24. data/lib/jekyll/filters/url_filters.rb +41 -14
  25. data/lib/jekyll/frontmatter_defaults.rb +15 -20
  26. data/lib/jekyll/hooks.rb +2 -5
  27. data/lib/jekyll/inclusion.rb +32 -0
  28. data/lib/jekyll/liquid_renderer.rb +18 -15
  29. data/lib/jekyll/liquid_renderer/file.rb +10 -0
  30. data/lib/jekyll/liquid_renderer/table.rb +18 -61
  31. data/lib/jekyll/mime.types +53 -11
  32. data/lib/jekyll/page.rb +26 -2
  33. data/lib/jekyll/page_excerpt.rb +25 -0
  34. data/lib/jekyll/path_manager.rb +31 -0
  35. data/lib/jekyll/profiler.rb +58 -0
  36. data/lib/jekyll/reader.rb +4 -1
  37. data/lib/jekyll/readers/collection_reader.rb +1 -0
  38. data/lib/jekyll/readers/data_reader.rb +1 -0
  39. data/lib/jekyll/readers/layout_reader.rb +1 -0
  40. data/lib/jekyll/readers/page_reader.rb +5 -5
  41. data/lib/jekyll/readers/post_reader.rb +2 -1
  42. data/lib/jekyll/readers/static_file_reader.rb +3 -3
  43. data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
  44. data/lib/jekyll/renderer.rb +9 -15
  45. data/lib/jekyll/site.rb +21 -12
  46. data/lib/jekyll/static_file.rb +15 -10
  47. data/lib/jekyll/tags/highlight.rb +2 -4
  48. data/lib/jekyll/tags/include.rb +67 -11
  49. data/lib/jekyll/tags/post_url.rb +8 -5
  50. data/lib/jekyll/theme.rb +19 -10
  51. data/lib/jekyll/url.rb +7 -3
  52. data/lib/jekyll/utils.rb +14 -18
  53. data/lib/jekyll/utils/platforms.rb +1 -1
  54. data/lib/jekyll/utils/win_tz.rb +1 -1
  55. data/lib/jekyll/version.rb +1 -1
  56. data/lib/theme_template/theme.gemspec.erb +1 -4
  57. metadata +33 -36
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Jekyll
4
4
  module Converters
5
- # Identify converter. Returns same content as given.
5
+ # Identity converter. Returns same content as given.
6
6
  # For more info on converters see https://jekyllrb.com/docs/plugins/converters/
7
7
  class Identity < Converter
8
8
  safe true
@@ -12,7 +12,7 @@ module Jekyll
12
12
  # Public: Does the given extension match this converter's list of acceptable extensions?
13
13
  # Takes one argument: the file's extension (including the dot).
14
14
  #
15
- # ext - The String extension to check (not relevant here)
15
+ # _ext - The String extension to check (not relevant here)
16
16
  #
17
17
  # Returns true since it always matches.
18
18
  def matches(_ext)
@@ -1,5 +1,73 @@
1
1
  # Frozen-string-literal: true
2
2
 
3
+ module Kramdown
4
+ # A Kramdown::Document subclass meant to optimize memory usage from initializing
5
+ # a kramdown document for parsing.
6
+ #
7
+ # The optimization is by using the same options Hash (and its derivatives) for
8
+ # converting all Markdown documents in a Jekyll site.
9
+ class JekyllDocument < Document
10
+ class << self
11
+ attr_reader :options, :parser
12
+
13
+ # The implementation is basically the core logic in +Kramdown::Document#initialize+
14
+ #
15
+ # rubocop:disable Naming/MemoizedInstanceVariableName
16
+ def setup(options)
17
+ @cache ||= {}
18
+
19
+ # reset variables on a subsequent set up with a different options Hash
20
+ unless @cache[:id] == options.hash
21
+ @options = @parser = nil
22
+ @cache[:id] = options.hash
23
+ end
24
+
25
+ @options ||= Options.merge(options).freeze
26
+ @parser ||= begin
27
+ parser_name = (@options[:input] || "kramdown").to_s
28
+ parser_name = parser_name[0..0].upcase + parser_name[1..-1]
29
+ try_require("parser", parser_name)
30
+
31
+ if Parser.const_defined?(parser_name)
32
+ Parser.const_get(parser_name)
33
+ else
34
+ raise Kramdown::Error, "kramdown has no parser to handle the specified " \
35
+ "input format: #{@options[:input]}"
36
+ end
37
+ end
38
+ end
39
+ # rubocop:enable Naming/MemoizedInstanceVariableName
40
+
41
+ private
42
+
43
+ def try_require(type, name)
44
+ require "kramdown/#{type}/#{Utils.snake_case(name)}"
45
+ rescue LoadError
46
+ false
47
+ end
48
+ end
49
+
50
+ def initialize(source, options = {})
51
+ JekyllDocument.setup(options)
52
+
53
+ @options = JekyllDocument.options
54
+ @root, @warnings = JekyllDocument.parser.parse(source, @options)
55
+ end
56
+
57
+ # Use Kramdown::Converter::Html class to convert this document into HTML.
58
+ #
59
+ # The implementation is basically an optimized version of core logic in
60
+ # +Kramdown::Document#method_missing+ from kramdown-2.1.0.
61
+ def to_html
62
+ output, warnings = Kramdown::Converter::Html.convert(@root, @options)
63
+ @warnings.concat(warnings)
64
+ output
65
+ end
66
+ end
67
+ end
68
+
69
+ #
70
+
3
71
  module Jekyll
4
72
  module Converters
5
73
  class Markdown
@@ -31,14 +99,14 @@ module Jekyll
31
99
  def setup
32
100
  @config["syntax_highlighter"] ||= highlighter
33
101
  @config["syntax_highlighter_opts"] ||= {}
102
+ @config["syntax_highlighter_opts"]["default_lang"] ||= "plaintext"
34
103
  @config["syntax_highlighter_opts"]["guess_lang"] = @config["guess_lang"]
35
104
  @config["coderay"] ||= {} # XXX: Legacy.
36
105
  modernize_coderay_config
37
- make_accessible
38
106
  end
39
107
 
40
108
  def convert(content)
41
- document = Kramdown::Document.new(content, @config)
109
+ document = Kramdown::JekyllDocument.new(content, @config)
42
110
  html_output = document.to_html
43
111
  if @config["show_warnings"]
44
112
  document.warnings.each do |warning|
@@ -64,13 +132,6 @@ module Jekyll
64
132
  end
65
133
  end
66
134
 
67
- def make_accessible(hash = @config)
68
- hash.keys.each do |key|
69
- hash[key.to_sym] = hash[key]
70
- make_accessible(hash[key]) if hash[key].is_a?(Hash)
71
- end
72
- end
73
-
74
135
  # config[kramdown][syntax_higlighter] >
75
136
  # config[kramdown][enable_coderay] >
76
137
  # config[highlighter]
@@ -39,7 +39,7 @@ module Jekyll
39
39
 
40
40
  begin
41
41
  self.content = File.read(@path || site.in_source_dir(base, name),
42
- Utils.merged_file_read_opts(site, opts))
42
+ **Utils.merged_file_read_opts(site, opts))
43
43
  if content =~ Document::YAML_FRONT_MATTER_REGEXP
44
44
  self.content = $POSTMATCH
45
45
  self.data = SafeYAML.load(Regexp.last_match(1))
@@ -78,7 +78,7 @@ module Jekyll
78
78
  #
79
79
  # Returns the transformed contents.
80
80
  def transform
81
- _renderer.convert(content)
81
+ renderer.convert(content)
82
82
  end
83
83
 
84
84
  # Determine the extension depending on content_type.
@@ -86,7 +86,7 @@ module Jekyll
86
86
  # Returns the String extension for the output file.
87
87
  # e.g. ".html" for an HTML output file.
88
88
  def output_ext
89
- _renderer.output_ext
89
+ renderer.output_ext
90
90
  end
91
91
 
92
92
  # Determine which converter to use based on this convertible's
@@ -94,7 +94,7 @@ module Jekyll
94
94
  #
95
95
  # Returns the Converter instance.
96
96
  def converters
97
- _renderer.converters
97
+ renderer.converters
98
98
  end
99
99
 
100
100
  # Render Liquid in the content
@@ -105,16 +105,17 @@ module Jekyll
105
105
  #
106
106
  # Returns the converted content
107
107
  def render_liquid(content, payload, info, path)
108
- _renderer.render_liquid(content, payload, info, path)
108
+ renderer.render_liquid(content, payload, info, path)
109
109
  end
110
110
 
111
111
  # Convert this Convertible's data to a Hash suitable for use by Liquid.
112
112
  #
113
113
  # Returns the Hash representation of this Convertible.
114
114
  def to_liquid(attrs = nil)
115
- further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map do |attribute|
116
- [attribute, send(attribute)]
117
- end]
115
+ further_data = \
116
+ (attrs || self.class::ATTRIBUTES_FOR_LIQUID).each_with_object({}) do |attribute, hsh|
117
+ hsh[attribute] = send(attribute)
118
+ end
118
119
 
119
120
  defaults = site.frontmatter_defaults.all(relative_path, type)
120
121
  Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
@@ -146,7 +147,7 @@ module Jekyll
146
147
  #
147
148
  # Returns true if extname == .sass or .scss, false otherwise.
148
149
  def sass_file?
149
- %w(.sass .scss).include?(ext)
150
+ Jekyll::Document::SASS_FILE_EXTS.include?(ext)
150
151
  end
151
152
 
152
153
  # Determine whether the document is a CoffeeScript file.
@@ -190,10 +191,10 @@ module Jekyll
190
191
  #
191
192
  # Returns nothing
192
193
  def render_all_layouts(layouts, payload, info)
193
- _renderer.layouts = layouts
194
- self.output = _renderer.place_in_layouts(output, payload, info)
194
+ renderer.layouts = layouts
195
+ self.output = renderer.place_in_layouts(output, payload, info)
195
196
  ensure
196
- @_renderer = nil # this will allow the modifications above to disappear
197
+ @renderer = nil # this will allow the modifications above to disappear
197
198
  end
198
199
 
199
200
  # Add any necessary layouts to this convertible document.
@@ -203,15 +204,15 @@ module Jekyll
203
204
  #
204
205
  # Returns nothing.
205
206
  def do_layout(payload, layouts)
206
- self.output = _renderer.tap do |renderer|
207
- renderer.layouts = layouts
208
- renderer.payload = payload
207
+ self.output = renderer.tap do |doc_renderer|
208
+ doc_renderer.layouts = layouts
209
+ doc_renderer.payload = payload
209
210
  end.run
210
211
 
211
212
  Jekyll.logger.debug "Post-Render Hooks:", relative_path
212
213
  Jekyll::Hooks.trigger hook_owner, :post_render, self
213
214
  ensure
214
- @_renderer = nil # this will allow the modifications above to disappear
215
+ @renderer = nil # this will allow the modifications above to disappear
215
216
  end
216
217
 
217
218
  # Write the generated page file to the destination directory.
@@ -240,12 +241,12 @@ module Jekyll
240
241
  end
241
242
  end
242
243
 
243
- private
244
-
245
- def _renderer
246
- @_renderer ||= Jekyll::Renderer.new(site, self)
244
+ def renderer
245
+ @renderer ||= Jekyll::Renderer.new(site, self)
247
246
  end
248
247
 
248
+ private
249
+
249
250
  def no_layout?
250
251
  data["layout"] == "none"
251
252
  end
@@ -5,7 +5,7 @@ module Jekyll
5
5
  include Comparable
6
6
  extend Forwardable
7
7
 
8
- attr_reader :path, :site, :extname, :collection
8
+ attr_reader :path, :site, :extname, :collection, :type
9
9
  attr_accessor :content, :output
10
10
 
11
11
  def_delegator :self, :read_post_data, :post_read
@@ -14,6 +14,23 @@ module Jekyll
14
14
  DATELESS_FILENAME_MATCHER = %r!^(?:.+/)*(.*)(\.[^.]+)$!.freeze
15
15
  DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
16
16
 
17
+ SASS_FILE_EXTS = %w(.sass .scss).freeze
18
+ YAML_FILE_EXTS = %w(.yaml .yml).freeze
19
+
20
+ #
21
+
22
+ # Class-wide cache to stash and retrieve regexp to detect "super-directories"
23
+ # of a particular Jekyll::Document object.
24
+ #
25
+ # dirname - The *special directory* for the Document.
26
+ # e.g. "_posts" or "_drafts" for Documents from the `site.posts` collection.
27
+ def self.superdirs_regex(dirname)
28
+ @superdirs_regex ||= {}
29
+ @superdirs_regex[dirname] ||= %r!#{dirname}.*!
30
+ end
31
+
32
+ #
33
+
17
34
  # Create a new Document.
18
35
  #
19
36
  # path - the path to the file
@@ -27,6 +44,8 @@ module Jekyll
27
44
  @path = path
28
45
  @extname = File.extname(path)
29
46
  @collection = relations[:collection]
47
+ @type = @collection.label.to_sym
48
+
30
49
  @has_yaml_header = nil
31
50
 
32
51
  if draft?
@@ -36,7 +55,7 @@ module Jekyll
36
55
  end
37
56
 
38
57
  data.default_proc = proc do |_, key|
39
- site.frontmatter_defaults.find(relative_path, collection.label, key)
58
+ site.frontmatter_defaults.find(relative_path, type, key)
40
59
  end
41
60
 
42
61
  trigger_hooks(:post_init)
@@ -97,7 +116,7 @@ module Jekyll
97
116
  #
98
117
  # Returns the output extension
99
118
  def output_ext
100
- @output_ext ||= Jekyll::Renderer.new(site, self).output_ext
119
+ renderer.output_ext
101
120
  end
102
121
 
103
122
  # The base filename of the document, without the file extname.
@@ -114,6 +133,10 @@ module Jekyll
114
133
  @basename ||= File.basename(path)
115
134
  end
116
135
 
136
+ def renderer
137
+ @renderer ||= Jekyll::Renderer.new(site, self)
138
+ end
139
+
117
140
  # Produces a "cleaned" relative path.
118
141
  # The "cleaned" relative path is the relative path without the extname
119
142
  # and with the collection's directory removed as well.
@@ -138,7 +161,7 @@ module Jekyll
138
161
  #
139
162
  # Returns true if the extname is either .yml or .yaml, false otherwise.
140
163
  def yaml_file?
141
- %w(.yaml .yml).include?(extname)
164
+ YAML_FILE_EXTS.include?(extname)
142
165
  end
143
166
 
144
167
  # Determine whether the document is an asset file.
@@ -154,7 +177,7 @@ module Jekyll
154
177
  #
155
178
  # Returns true if extname == .sass or .scss, false otherwise.
156
179
  def sass_file?
157
- %w(.sass .scss).include?(extname)
180
+ SASS_FILE_EXTS.include?(extname)
158
181
  end
159
182
 
160
183
  # Determine whether the document is a CoffeeScript file.
@@ -279,7 +302,7 @@ module Jekyll
279
302
  else
280
303
  begin
281
304
  merge_defaults
282
- read_content(opts)
305
+ read_content(**opts)
283
306
  read_post_data
284
307
  rescue StandardError => e
285
308
  handle_read_error(e)
@@ -373,12 +396,6 @@ module Jekyll
373
396
  @related_posts ||= Jekyll::RelatedPosts.new(self).build
374
397
  end
375
398
 
376
- # Override of normal respond_to? to match method_missing's logic for
377
- # looking in @data.
378
- def respond_to?(method, include_private = false)
379
- data.key?(method.to_s) || super
380
- end
381
-
382
399
  # Override of method_missing to check in @data for the key.
383
400
  def method_missing(method, *args, &blck)
384
401
  if data.key?(method.to_s)
@@ -401,28 +418,33 @@ module Jekyll
401
418
  #
402
419
  # Returns nothing.
403
420
  def categories_from_path(special_dir)
404
- superdirs = relative_path.sub(%r!#{special_dir}(.*)!, "")
405
- .split(File::SEPARATOR)
406
- .reject do |c|
407
- c.empty? || c == special_dir || c == basename
421
+ if relative_path.start_with?(special_dir)
422
+ superdirs = []
423
+ else
424
+ superdirs = relative_path.sub(Document.superdirs_regex(special_dir), "")
425
+ superdirs = superdirs.split(File::SEPARATOR)
426
+ superdirs.reject! { |c| c.empty? || c == special_dir || c == basename }
408
427
  end
428
+
409
429
  merge_data!({ "categories" => superdirs }, :source => "file path")
410
430
  end
411
431
 
412
432
  def populate_categories
413
- merge_data!(
414
- "categories" => (
415
- Array(data["categories"]) + Utils.pluralized_array_from_hash(
416
- data, "category", "categories"
417
- )
418
- ).map(&:to_s).flatten.uniq
433
+ categories = Array(data["categories"]) + Utils.pluralized_array_from_hash(
434
+ data, "category", "categories"
419
435
  )
436
+ categories.map!(&:to_s)
437
+ categories.flatten!
438
+ categories.uniq!
439
+
440
+ merge_data!({ "categories" => categories })
420
441
  end
421
442
 
422
443
  def populate_tags
423
- merge_data!(
424
- "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
425
- )
444
+ tags = Utils.pluralized_array_from_hash(data, "tag", "tags")
445
+ tags.flatten!
446
+
447
+ merge_data!({ "tags" => tags })
426
448
  end
427
449
 
428
450
  private
@@ -444,15 +466,12 @@ module Jekyll
444
466
  end
445
467
 
446
468
  def merge_defaults
447
- defaults = @site.frontmatter_defaults.all(
448
- relative_path,
449
- collection.label.to_sym
450
- )
469
+ defaults = @site.frontmatter_defaults.all(relative_path, type)
451
470
  merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?
452
471
  end
453
472
 
454
- def read_content(opts)
455
- self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
473
+ def read_content(**opts)
474
+ self.content = File.read(path, **Utils.merged_file_read_opts(site, opts))
456
475
  if content =~ YAML_FRONT_MATTER_REGEXP
457
476
  self.content = $POSTMATCH
458
477
  data_file = SafeYAML.load(Regexp.last_match(1))
@@ -479,6 +498,7 @@ module Jekyll
479
498
  end
480
499
  end
481
500
 
501
+ # rubocop:disable Metrics/AbcSize
482
502
  def populate_title
483
503
  if relative_path =~ DATE_FILENAME_MATCHER
484
504
  date, slug, ext = Regexp.last_match.captures
@@ -486,6 +506,10 @@ module Jekyll
486
506
  elsif relative_path =~ DATELESS_FILENAME_MATCHER
487
507
  slug, ext = Regexp.last_match.captures
488
508
  end
509
+ # `slug` will be nil for documents without an extension since the regex patterns
510
+ # above tests for an extension as well.
511
+ # In such cases, assign `basename_without_ext` as the slug.
512
+ slug ||= basename_without_ext
489
513
 
490
514
  # slugs shouldn't end with a period
491
515
  # `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
@@ -497,6 +521,7 @@ module Jekyll
497
521
  data["slug"] ||= slug
498
522
  data["ext"] ||= ext
499
523
  end
524
+ # rubocop:enable Metrics/AbcSize
500
525
 
501
526
  def modify_date(date)
502
527
  if !data["date"] || data["date"].to_i == site.time.to_i
@@ -64,6 +64,18 @@ module Jekyll
64
64
  result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
65
65
  end
66
66
  end
67
+
68
+ def title
69
+ @obj.data["title"]
70
+ end
71
+
72
+ def categories
73
+ @obj.data["categories"]
74
+ end
75
+
76
+ def tags
77
+ @obj.data["tags"]
78
+ end
67
79
  end
68
80
  end
69
81
  end