jekyll 4.0.0.pre.alpha1 → 4.1.1

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 (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
@@ -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
@@ -47,7 +48,8 @@ module Jekyll
47
48
  end
48
49
 
49
50
  process(name)
50
- read_yaml(File.join(base, dir), name)
51
+ read_yaml(PathManager.join(base, dir), name)
52
+ generate_excerpt if site.config["page_excerpts"]
51
53
 
52
54
  data.default_proc = proc do |_, key|
53
55
  site.frontmatter_defaults.find(relative_path, type, key)
@@ -145,7 +147,7 @@ module Jekyll
145
147
 
146
148
  # The path to the page source file, relative to the site source
147
149
  def relative_path
148
- @relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A\/!, "")
150
+ @relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A/!, "")
149
151
  end
150
152
 
151
153
  # Obtain destination path.
@@ -182,5 +184,27 @@ module Jekyll
182
184
  def write?
183
185
  true
184
186
  end
187
+
188
+ def excerpt_separator
189
+ @excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
190
+ end
191
+
192
+ def excerpt
193
+ return @excerpt if defined?(@excerpt)
194
+
195
+ @excerpt = data["excerpt"]&.to_s
196
+ end
197
+
198
+ def generate_excerpt?
199
+ !excerpt_separator.empty? && self.class == Jekyll::Page && html?
200
+ end
201
+
202
+ private
203
+
204
+ def generate_excerpt
205
+ return unless generate_excerpt?
206
+
207
+ data["excerpt"] ||= Jekyll::PageExcerpt.new(self)
208
+ end
185
209
  end
186
210
  end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class PageExcerpt < Excerpt
5
+ attr_reader :doc
6
+ alias_method :id, :relative_path
7
+
8
+ EXCERPT_ATTRIBUTES = (Page::ATTRIBUTES_FOR_LIQUID - %w(excerpt)).freeze
9
+ private_constant :EXCERPT_ATTRIBUTES
10
+
11
+ def to_liquid
12
+ @to_liquid ||= doc.to_liquid(EXCERPT_ATTRIBUTES)
13
+ end
14
+
15
+ def render_with_liquid?
16
+ return false if data["render_with_liquid"] == false
17
+
18
+ Jekyll::Utils.has_liquid_construct?(content)
19
+ end
20
+
21
+ def inspect
22
+ "#<#{self.class} id=#{id.inspect}>"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ # A singleton class that caches frozen instances of path strings returned from its methods.
5
+ #
6
+ # NOTE:
7
+ # This class exists because `File.join` allocates an Array and returns a new String on every
8
+ # call using **the same arguments**. Caching the result means reduced memory usage.
9
+ # However, the caches are never flushed so that they can be used even when a site is
10
+ # regenerating. The results are frozen to deter mutation of the cached string.
11
+ #
12
+ # Therefore, employ this class only for situations where caching the result is necessary
13
+ # for performance reasons.
14
+ #
15
+ class PathManager
16
+ # This class cannot be initialized from outside
17
+ private_class_method :new
18
+
19
+ # Wraps `File.join` to cache the frozen result.
20
+ # Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand.
21
+ #
22
+ # Returns a frozen string.
23
+ def self.join(base, item)
24
+ base = "" if base.nil? || base.empty?
25
+ item = "" if item.nil? || item.empty?
26
+ @join ||= {}
27
+ @join[base] ||= {}
28
+ @join[base][item] ||= File.join(base, item).freeze
29
+ end
30
+ end
31
+ 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
@@ -85,7 +85,7 @@ module Jekyll
85
85
  def retrieve_dirs(_base, dir, dot_dirs)
86
86
  dot_dirs.each do |file|
87
87
  dir_path = site.in_source_dir(dir, file)
88
- rel_path = File.join(dir, file)
88
+ rel_path = PathManager.join(dir, file)
89
89
  @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
90
90
  end
91
91
  end
@@ -161,11 +161,14 @@ module Jekyll
161
161
  end
162
162
 
163
163
  def read_included_excludes
164
+ entry_filter = EntryFilter.new(site)
165
+
164
166
  site.include.each do |entry|
165
167
  next if entry == ".htaccess"
166
168
 
167
169
  entry_path = site.in_source_dir(entry)
168
170
  next if File.directory?(entry_path)
171
+ next if entry_filter.symlink?(entry_path)
169
172
 
170
173
  read_included_file(entry_path) if File.file?(entry_path)
171
174
  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,20 +3,20 @@
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
9
10
  @unfiltered_content = []
10
11
  end
11
12
 
12
- # Read all the files in <source>/<dir>/ for Yaml header and create a new Page
13
- # object for each file.
13
+ # Create a new `Jekyll::Page` object for each entry in a given array.
14
14
  #
15
- # dir - The String relative path of the directory to read.
15
+ # files - An array of file names inside `@dir`
16
16
  #
17
- # Returns an array of static pages.
17
+ # Returns an array of publishable `Jekyll::Page` objects.
18
18
  def read(files)
19
- files.map do |page|
19
+ files.each do |page|
20
20
  @unfiltered_content << Page.new(@site, @site.source, @dir, page)
21
21
  end
22
22
  @unfiltered_content.select { |page| site.publisher.publish?(page) }
@@ -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
@@ -50,7 +51,7 @@ module Jekyll
50
51
  # Returns klass type of content files
51
52
  def read_content(dir, magic_dir, matcher)
52
53
  @site.reader.get_entries(dir, magic_dir).map do |entry|
53
- next unless entry =~ matcher
54
+ next unless matcher.match?(entry)
54
55
 
55
56
  path = @site.in_source_dir(File.join(dir, magic_dir, entry))
56
57
  Document.new(path,
@@ -3,16 +3,16 @@
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
9
10
  @unfiltered_content = []
10
11
  end
11
12
 
12
- # Read all the files in <source>/<dir>/ for Yaml header and create a new Page
13
- # object for each file.
13
+ # Create a new StaticFile object for every entry in a given list of basenames.
14
14
  #
15
- # dir - The String relative path of the directory to read.
15
+ # files - an array of file basenames.
16
16
  #
17
17
  # Returns an array of static files.
18
18
  def read(files)
@@ -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
  #
@@ -145,9 +151,9 @@ module Jekyll
145
151
  #
146
152
  # Returns a Hash containing collection name-to-instance pairs.
147
153
  def collections
148
- @collections ||= Hash[collection_names.map do |coll|
149
- [coll, Jekyll::Collection.new(self, coll)]
150
- end]
154
+ @collections ||= collection_names.each_with_object({}) do |name, hsh|
155
+ hsh[name] = Jekyll::Collection.new(self, name)
156
+ end
151
157
  end
152
158
 
153
159
  # The list of collection names.
@@ -326,15 +332,15 @@ module Jekyll
326
332
  #
327
333
  # Returns an Array of Documents which should be written
328
334
  def docs_to_write
329
- @docs_to_write ||= documents.select(&:write?)
335
+ documents.select(&:write?)
330
336
  end
331
337
 
332
338
  # Get all the documents
333
339
  #
334
340
  # Returns an Array of all Documents
335
341
  def documents
336
- @documents ||= collections.reduce(Set.new) do |docs, (_, collection)|
337
- docs + collection.docs + collection.files
342
+ collections.each_with_object(Set.new) do |(_, collection), set|
343
+ set.merge(collection.docs).merge(collection.files)
338
344
  end.to_a
339
345
  end
340
346
 
@@ -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
 
@@ -469,8 +477,8 @@ module Jekyll
469
477
 
470
478
  # Disable Marshaling cache to disk in Safe Mode
471
479
  def configure_cache
472
- Jekyll::Cache.base_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
473
- Jekyll::Cache.disable_disk_cache! if safe
480
+ Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
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