jekyll 4.0.1 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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