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
@@ -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