jekyll 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +48 -19
  3. data/lib/jekyll.rb +3 -0
  4. data/lib/jekyll/collection.rb +1 -1
  5. data/lib/jekyll/command.rb +4 -2
  6. data/lib/jekyll/commands/new.rb +2 -2
  7. data/lib/jekyll/commands/serve.rb +9 -1
  8. data/lib/jekyll/configuration.rb +1 -1
  9. data/lib/jekyll/converters/identity.rb +2 -2
  10. data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -1
  11. data/lib/jekyll/convertible.rb +15 -15
  12. data/lib/jekyll/document.rb +18 -4
  13. data/lib/jekyll/drops/document_drop.rb +12 -0
  14. data/lib/jekyll/drops/page_drop.rb +18 -0
  15. data/lib/jekyll/drops/url_drop.rb +8 -0
  16. data/lib/jekyll/entry_filter.rb +19 -6
  17. data/lib/jekyll/excerpt.rb +1 -1
  18. data/lib/jekyll/filters.rb +99 -14
  19. data/lib/jekyll/filters/url_filters.rb +41 -14
  20. data/lib/jekyll/frontmatter_defaults.rb +12 -17
  21. data/lib/jekyll/hooks.rb +2 -5
  22. data/lib/jekyll/inclusion.rb +32 -0
  23. data/lib/jekyll/liquid_renderer.rb +18 -15
  24. data/lib/jekyll/liquid_renderer/table.rb +1 -21
  25. data/lib/jekyll/page.rb +43 -0
  26. data/lib/jekyll/page_excerpt.rb +26 -0
  27. data/lib/jekyll/profiler.rb +58 -0
  28. data/lib/jekyll/readers/collection_reader.rb +1 -0
  29. data/lib/jekyll/readers/data_reader.rb +1 -0
  30. data/lib/jekyll/readers/layout_reader.rb +1 -0
  31. data/lib/jekyll/readers/page_reader.rb +1 -0
  32. data/lib/jekyll/readers/post_reader.rb +1 -0
  33. data/lib/jekyll/readers/static_file_reader.rb +1 -0
  34. data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
  35. data/lib/jekyll/renderer.rb +9 -15
  36. data/lib/jekyll/site.rb +14 -5
  37. data/lib/jekyll/static_file.rb +14 -9
  38. data/lib/jekyll/tags/include.rb +58 -3
  39. data/lib/jekyll/theme.rb +6 -0
  40. data/lib/jekyll/utils.rb +4 -4
  41. data/lib/jekyll/utils/win_tz.rb +1 -1
  42. data/lib/jekyll/version.rb +1 -1
  43. data/lib/theme_template/theme.gemspec.erb +1 -4
  44. metadata +14 -31
@@ -91,11 +91,8 @@ module Jekyll
91
91
  # interface for Jekyll core components to trigger hooks
92
92
  def self.trigger(owner, event, *args)
93
93
  # proceed only if there are hooks to call
94
- return unless @registry[owner]
95
- return unless @registry[owner][event]
96
-
97
- # hooks to call for this owner and event
98
- hooks = @registry[owner][event]
94
+ hooks = @registry.dig(owner, event)
95
+ return if hooks.nil? || hooks.empty?
99
96
 
100
97
  # sort and call hooks according to priority and load order
101
98
  hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class Inclusion
5
+ attr_reader :site, :name, :path
6
+ private :site
7
+
8
+ def initialize(site, base, name)
9
+ @site = site
10
+ @name = name
11
+ @path = PathManager.join(base, name)
12
+ end
13
+
14
+ def render(context)
15
+ @template ||= site.liquid_renderer.file(path).parse(content)
16
+ @template.render!(context)
17
+ rescue Liquid::Error => e
18
+ e.template_name = path
19
+ e.markup_context = "included " if e.markup_context.nil?
20
+ raise e
21
+ end
22
+
23
+ def content
24
+ @content ||= File.read(path, **site.file_read_opts)
25
+ end
26
+
27
+ def inspect
28
+ "#{self.class} #{path.inspect}"
29
+ end
30
+ alias_method :to_s, :inspect
31
+ end
32
+ end
@@ -5,11 +5,6 @@ require_relative "liquid_renderer/table"
5
5
 
6
6
  module Jekyll
7
7
  class LiquidRenderer
8
- extend Forwardable
9
-
10
- private def_delegator :@site, :in_source_dir, :source_dir
11
- private def_delegator :@site, :in_theme_dir, :theme_dir
12
-
13
8
  def initialize(site)
14
9
  @site = site
15
10
  Liquid::Template.error_mode = @site.config["liquid"]["error_mode"].to_sym
@@ -22,13 +17,7 @@ module Jekyll
22
17
  end
23
18
 
24
19
  def file(filename)
25
- filename.match(filename_regex)
26
- filename =
27
- if Regexp.last_match(1) == theme_dir("")
28
- ::File.join(::File.basename(Regexp.last_match(1)), Regexp.last_match(2))
29
- else
30
- Regexp.last_match(2)
31
- end
20
+ filename = normalize_path(filename)
32
21
  LiquidRenderer::File.new(self, filename).tap do
33
22
  @stats[filename] ||= new_profile_hash
34
23
  end
@@ -64,9 +53,23 @@ module Jekyll
64
53
 
65
54
  private
66
55
 
67
- def filename_regex
68
- @filename_regex ||= begin
69
- %r!\A(#{Regexp.escape(source_dir)}/|#{Regexp.escape(theme_dir.to_s)}/|/*)(.*)!i
56
+ def normalize_path(filename)
57
+ @normalize_path ||= {}
58
+ @normalize_path[filename] ||= begin
59
+ theme_dir = @site.theme&.root
60
+ case filename
61
+ when %r!\A(#{Regexp.escape(@site.source)}/)(?<rest>.*)!io
62
+ Regexp.last_match(:rest)
63
+ when %r!(/gems/.*)*/gems/(?<dirname>[^/]+)(?<rest>.*)!,
64
+ %r!(?<dirname>[^/]+/lib)(?<rest>.*)!
65
+ "#{Regexp.last_match(:dirname)}#{Regexp.last_match(:rest)}"
66
+ when theme_dir && %r!\A#{Regexp.escape(theme_dir)}/(?<rest>.*)!io
67
+ PathManager.join(@site.theme.basename, Regexp.last_match(:rest))
68
+ when %r!\A/(.*)!
69
+ Regexp.last_match(1)
70
+ else
71
+ filename
72
+ end
70
73
  end
71
74
  end
72
75
 
@@ -10,31 +10,11 @@ module Jekyll
10
10
  end
11
11
 
12
12
  def to_s(num_of_rows = 50)
13
- tabulate(data_for_table(num_of_rows))
13
+ Jekyll::Profiler.tabulate(data_for_table(num_of_rows))
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def tabulate(data)
19
- require "terminal-table"
20
-
21
- header = data.shift
22
- footer = data.pop
23
- output = +"\n"
24
-
25
- table = Terminal::Table.new do |t|
26
- t << header
27
- t << :separator
28
- data.each { |row| t << row }
29
- t << :separator
30
- t << footer
31
- t.style = { :alignment => :right, :border_top => false, :border_bottom => false }
32
- t.align_column(0, :left)
33
- end
34
-
35
- output << table.to_s << "\n"
36
- end
37
-
38
18
  # rubocop:disable Metrics/AbcSize
39
19
  def data_for_table(num_of_rows)
40
20
  sorted = @stats.sort_by { |_, file_stats| -file_stats[:time] }
@@ -15,6 +15,7 @@ module Jekyll
15
15
  ATTRIBUTES_FOR_LIQUID = %w(
16
16
  content
17
17
  dir
18
+ excerpt
18
19
  name
19
20
  path
20
21
  url
@@ -70,6 +71,38 @@ module Jekyll
70
71
  end
71
72
  end
72
73
 
74
+ # For backwards-compatibility in subclasses that do not redefine
75
+ # the `:to_liquid` method, stash existing definition under a new name
76
+ #
77
+ # TODO: Remove in Jekyll 5.0
78
+ alias_method :legacy_to_liquid, :to_liquid
79
+ private :legacy_to_liquid
80
+
81
+ # Private
82
+ # Subclasses can choose to optimize their `:to_liquid` method by wrapping
83
+ # it around this definition.
84
+ #
85
+ # TODO: Remove in Jekyll 5.0
86
+ def liquid_drop
87
+ @liquid_drop ||= begin
88
+ defaults = site.frontmatter_defaults.all(relative_path, type)
89
+ unless defaults.empty?
90
+ Utils.deep_merge_hashes!(data, Utils.deep_merge_hashes!(defaults, data))
91
+ end
92
+ Drops::PageDrop.new(self)
93
+ end
94
+ end
95
+ private :liquid_drop
96
+
97
+ # Public
98
+ #
99
+ # Liquid representation of current page
100
+ #
101
+ # TODO: Remove optional parameter in Jekyll 5.0
102
+ def to_liquid(attrs = nil)
103
+ self.class == Jekyll::Page ? liquid_drop : legacy_to_liquid(attrs)
104
+ end
105
+
73
106
  # The full path and filename of the post. Defined in the YAML of the post
74
107
  # body.
75
108
  #
@@ -182,5 +215,15 @@ module Jekyll
182
215
  def write?
183
216
  true
184
217
  end
218
+
219
+ def excerpt_separator
220
+ @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
221
+ end
222
+
223
+ def excerpt
224
+ return data["excerpt"] unless self.class == Jekyll::Page
225
+
226
+ data["excerpt"] ||= Jekyll::PageExcerpt.new(self).to_liquid unless excerpt_separator.empty?
227
+ end
185
228
  end
186
229
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class PageExcerpt < Excerpt
5
+ attr_reader :output, :doc
6
+ alias_method :id, :relative_path
7
+
8
+ # The Liquid representation of this instance is simply the rendered output string.
9
+ alias_method :to_liquid, :output
10
+
11
+ def initialize(doc)
12
+ super
13
+ self.output = Renderer.new(site, self, site.site_payload).run
14
+ end
15
+
16
+ def render_with_liquid?
17
+ return false if data["render_with_liquid"] == false
18
+
19
+ Jekyll::Utils.has_liquid_construct?(content)
20
+ end
21
+
22
+ def inspect
23
+ "#<#{self.class} id=#{id.inspect}>"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class Profiler
5
+ TERMINAL_TABLE_STYLES = {
6
+ :alignment => :right,
7
+ :border_top => false,
8
+ :border_bottom => false,
9
+ }.freeze
10
+ private_constant :TERMINAL_TABLE_STYLES
11
+
12
+ def self.tabulate(table_rows)
13
+ require "terminal-table"
14
+
15
+ rows = table_rows.dup
16
+ header = rows.shift
17
+ footer = rows.pop
18
+ output = +"\n"
19
+
20
+ table = Terminal::Table.new do |t|
21
+ t << header
22
+ t << :separator
23
+ rows.each { |row| t << row }
24
+ t << :separator
25
+ t << footer
26
+ t.style = TERMINAL_TABLE_STYLES
27
+ t.align_column(0, :left)
28
+ end
29
+
30
+ output << table.to_s << "\n"
31
+ end
32
+
33
+ def initialize(site)
34
+ @site = site
35
+ end
36
+
37
+ def profile_process
38
+ profile_data = { "PHASE" => "TIME" }
39
+ total_time = 0
40
+
41
+ [:reset, :read, :generate, :render, :cleanup, :write].each do |method|
42
+ start_time = Time.now
43
+ @site.send(method)
44
+ end_time = (Time.now - start_time).round(4)
45
+ profile_data[method.to_s.upcase] = format("%.4f", end_time)
46
+ total_time += end_time
47
+ end
48
+
49
+ profile_data["TOTAL TIME"] = format("%.4f", total_time)
50
+
51
+ Jekyll.logger.info "\nBuild Process Summary:"
52
+ Jekyll.logger.info Profiler.tabulate(Array(profile_data))
53
+
54
+ Jekyll.logger.info "\nSite Render Stats:"
55
+ @site.print_stats
56
+ end
57
+ end
58
+ end
@@ -5,6 +5,7 @@ module Jekyll
5
5
  SPECIAL_COLLECTIONS = %w(posts data).freeze
6
6
 
7
7
  attr_reader :site, :content
8
+
8
9
  def initialize(site)
9
10
  @site = site
10
11
  @content = {}
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class DataReader
5
5
  attr_reader :site, :content
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  @content = {}
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class LayoutReader
5
5
  attr_reader :site
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  @layouts = {}
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class PageReader
5
5
  attr_reader :site, :dir, :unfiltered_content
6
+
6
7
  def initialize(site, dir)
7
8
  @site = site
8
9
  @dir = dir
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class PostReader
5
5
  attr_reader :site, :unfiltered_content
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  end
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class StaticFileReader
5
5
  attr_reader :site, :dir, :unfiltered_content
6
+
6
7
  def initialize(site, dir)
7
8
  @site = site
8
9
  @dir = dir
@@ -3,6 +3,7 @@
3
3
  module Jekyll
4
4
  class ThemeAssetsReader
5
5
  attr_reader :site
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  end
@@ -66,7 +66,7 @@ module Jekyll
66
66
  # Render the document.
67
67
  #
68
68
  # Returns String rendered document output
69
- # rubocop: disable AbcSize
69
+ # rubocop: disable Metrics/AbcSize
70
70
  def render_document
71
71
  info = {
72
72
  :registers => { :site => site, :page => payload["page"] },
@@ -91,7 +91,7 @@ module Jekyll
91
91
 
92
92
  output
93
93
  end
94
- # rubocop: enable AbcSize
94
+ # rubocop: enable Metrics/AbcSize
95
95
 
96
96
  # Convert the document using the converters which match this renderer's document.
97
97
  #
@@ -125,13 +125,13 @@ module Jekyll
125
125
  LiquidRenderer.format_error(e, path || document.relative_path)
126
126
  end
127
127
  template.render!(payload, info)
128
- # rubocop: disable RescueException
128
+ # rubocop: disable Lint/RescueException
129
129
  rescue Exception => e
130
130
  Jekyll.logger.error "Liquid Exception:",
131
131
  LiquidRenderer.format_error(e, path || document.relative_path)
132
132
  raise e
133
133
  end
134
- # rubocop: enable RescueException
134
+ # rubocop: enable Lint/RescueException
135
135
 
136
136
  # Checks if the layout specified in the document actually exists
137
137
  #
@@ -174,16 +174,10 @@ module Jekyll
174
174
  # layout - the layout to check
175
175
  # Returns nothing
176
176
  def validate_layout(layout)
177
- if invalid_layout?(layout)
178
- Jekyll.logger.warn(
179
- "Build Warning:",
180
- "Layout '#{document.data["layout"]}' requested "\
181
- "in #{document.relative_path} does not exist."
182
- )
183
- elsif !layout.nil?
184
- layout_source = layout.path.start_with?(site.source) ? :site : :theme
185
- Jekyll.logger.debug "Layout source:", layout_source
186
- end
177
+ return unless invalid_layout?(layout)
178
+
179
+ Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
180
+ "in #{document.relative_path} does not exist."
187
181
  end
188
182
 
189
183
  # Render layout content into document.output
@@ -197,7 +191,7 @@ module Jekyll
197
191
  layout.content,
198
192
  payload,
199
193
  info,
200
- layout.relative_path
194
+ layout.path
201
195
  )
202
196
  end
203
197
 
@@ -3,14 +3,14 @@
3
3
  module Jekyll
4
4
  class Site
5
5
  attr_reader :source, :dest, :cache_dir, :config
6
- attr_accessor :layouts, :pages, :static_files, :drafts,
6
+ attr_accessor :layouts, :pages, :static_files, :drafts, :inclusions,
7
7
  :exclude, :include, :lsi, :highlighter, :permalink_style,
8
8
  :time, :future, :unpublished, :safe, :plugins, :limit_posts,
9
9
  :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
10
10
  :gems, :plugin_manager, :theme
11
11
 
12
12
  attr_accessor :converters, :generators, :reader
13
- attr_reader :regenerator, :liquid_renderer, :includes_load_paths
13
+ attr_reader :regenerator, :liquid_renderer, :includes_load_paths, :filter_cache, :profiler
14
14
 
15
15
  # Public: Initialize a new Site.
16
16
  #
@@ -23,8 +23,10 @@ module Jekyll
23
23
  self.config = config
24
24
 
25
25
  @cache_dir = in_source_dir(config["cache_dir"])
26
+ @filter_cache = {}
26
27
 
27
28
  @reader = Reader.new(self)
29
+ @profiler = Profiler.new(self)
28
30
  @regenerator = Regenerator.new(self)
29
31
  @liquid_renderer = LiquidRenderer.new(self)
30
32
 
@@ -70,19 +72,21 @@ module Jekyll
70
72
  #
71
73
  # Returns nothing.
72
74
  def process
75
+ return profiler.profile_process if config["profile"]
76
+
73
77
  reset
74
78
  read
75
79
  generate
76
80
  render
77
81
  cleanup
78
82
  write
79
- print_stats if config["profile"]
80
83
  end
81
84
 
82
85
  def print_stats
83
86
  Jekyll.logger.info @liquid_renderer.stats_table
84
87
  end
85
88
 
89
+ # rubocop:disable Metrics/AbcSize
86
90
  # rubocop:disable Metrics/MethodLength
87
91
  #
88
92
  # Reset Site details.
@@ -95,6 +99,7 @@ module Jekyll
95
99
  Time.now
96
100
  end
97
101
  self.layouts = {}
102
+ self.inclusions = {}
98
103
  self.pages = []
99
104
  self.static_files = []
100
105
  self.data = {}
@@ -114,6 +119,7 @@ module Jekyll
114
119
  Jekyll::Hooks.trigger :site, :after_reset, self
115
120
  end
116
121
  # rubocop:enable Metrics/MethodLength
122
+ # rubocop:enable Metrics/AbcSize
117
123
 
118
124
  # Load necessary libraries, plugins, converters, and generators.
119
125
  #
@@ -431,6 +437,8 @@ module Jekyll
431
437
  private
432
438
 
433
439
  def load_theme_configuration(config)
440
+ return config if config["ignore_theme_config"] == true
441
+
434
442
  theme_config_file = in_theme_dir("_config.yml")
435
443
  return config unless File.exist?(theme_config_file)
436
444
 
@@ -470,7 +478,7 @@ module Jekyll
470
478
  # Disable Marshaling cache to disk in Safe Mode
471
479
  def configure_cache
472
480
  Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
473
- Jekyll::Cache.disable_disk_cache! if safe
481
+ Jekyll::Cache.disable_disk_cache! if safe || config["disable_disk_cache"]
474
482
  end
475
483
 
476
484
  def configure_plugins
@@ -520,7 +528,8 @@ module Jekyll
520
528
  def render_regenerated(document, payload)
521
529
  return unless regenerator.regenerate?(document)
522
530
 
523
- document.output = Jekyll::Renderer.new(self, document, payload).run
531
+ document.renderer.payload = payload
532
+ document.output = document.renderer.run
524
533
  document.trigger_hooks(:post_render)
525
534
  end
526
535
  end