jekyll 4.0.0.pre.beta1 → 4.2.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +204 -18
  3. data/README.markdown +2 -6
  4. data/lib/blank_template/_layouts/default.html +1 -1
  5. data/lib/jekyll.rb +6 -17
  6. data/lib/jekyll/cleaner.rb +3 -3
  7. data/lib/jekyll/collection.rb +2 -2
  8. data/lib/jekyll/command.rb +4 -2
  9. data/lib/jekyll/commands/doctor.rb +19 -15
  10. data/lib/jekyll/commands/new.rb +4 -4
  11. data/lib/jekyll/commands/new_theme.rb +0 -2
  12. data/lib/jekyll/commands/serve.rb +12 -1
  13. data/lib/jekyll/configuration.rb +18 -19
  14. data/lib/jekyll/converters/identity.rb +2 -2
  15. data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -1
  16. data/lib/jekyll/convertible.rb +30 -23
  17. data/lib/jekyll/document.rb +41 -19
  18. data/lib/jekyll/drops/collection_drop.rb +3 -3
  19. data/lib/jekyll/drops/document_drop.rb +4 -3
  20. data/lib/jekyll/drops/drop.rb +98 -20
  21. data/lib/jekyll/drops/site_drop.rb +3 -3
  22. data/lib/jekyll/drops/static_file_drop.rb +4 -4
  23. data/lib/jekyll/drops/url_drop.rb +11 -3
  24. data/lib/jekyll/entry_filter.rb +18 -7
  25. data/lib/jekyll/excerpt.rb +1 -1
  26. data/lib/jekyll/filters.rb +112 -28
  27. data/lib/jekyll/filters/url_filters.rb +45 -15
  28. data/lib/jekyll/frontmatter_defaults.rb +14 -19
  29. data/lib/jekyll/hooks.rb +22 -21
  30. data/lib/jekyll/inclusion.rb +32 -0
  31. data/lib/jekyll/layout.rb +5 -0
  32. data/lib/jekyll/liquid_renderer.rb +18 -15
  33. data/lib/jekyll/liquid_renderer/file.rb +10 -0
  34. data/lib/jekyll/liquid_renderer/table.rb +1 -64
  35. data/lib/jekyll/page.rb +42 -11
  36. data/lib/jekyll/page_excerpt.rb +25 -0
  37. data/lib/jekyll/path_manager.rb +53 -10
  38. data/lib/jekyll/profiler.rb +58 -0
  39. data/lib/jekyll/reader.rb +11 -6
  40. data/lib/jekyll/readers/collection_reader.rb +1 -0
  41. data/lib/jekyll/readers/data_reader.rb +4 -0
  42. data/lib/jekyll/readers/layout_reader.rb +1 -0
  43. data/lib/jekyll/readers/page_reader.rb +1 -0
  44. data/lib/jekyll/readers/post_reader.rb +2 -1
  45. data/lib/jekyll/readers/static_file_reader.rb +1 -0
  46. data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
  47. data/lib/jekyll/related_posts.rb +1 -1
  48. data/lib/jekyll/renderer.rb +15 -17
  49. data/lib/jekyll/site.rb +34 -10
  50. data/lib/jekyll/static_file.rb +17 -12
  51. data/lib/jekyll/tags/include.rb +82 -33
  52. data/lib/jekyll/tags/link.rb +2 -1
  53. data/lib/jekyll/tags/post_url.rb +3 -4
  54. data/lib/jekyll/theme.rb +6 -8
  55. data/lib/jekyll/url.rb +8 -5
  56. data/lib/jekyll/utils.rb +5 -5
  57. data/lib/jekyll/utils/platforms.rb +34 -49
  58. data/lib/jekyll/utils/win_tz.rb +1 -1
  59. data/lib/jekyll/version.rb +1 -1
  60. data/lib/theme_template/theme.gemspec.erb +1 -4
  61. metadata +34 -39
@@ -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
@@ -16,16 +16,59 @@ module Jekyll
16
16
  # This class cannot be initialized from outside
17
17
  private_class_method :new
18
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
19
+ class << self
20
+ # Wraps `File.join` to cache the frozen result.
21
+ # Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand.
22
+ #
23
+ # Returns a frozen string.
24
+ def join(base, item)
25
+ base = "" if base.nil? || base.empty?
26
+ item = "" if item.nil? || item.empty?
27
+ @join ||= {}
28
+ @join[base] ||= {}
29
+ @join[base][item] ||= File.join(base, item).freeze
30
+ end
31
+
32
+ # Ensures the questionable path is prefixed with the base directory
33
+ # and prepends the questionable path with the base directory if false.
34
+ #
35
+ # Returns a frozen string.
36
+ def sanitized_path(base_directory, questionable_path)
37
+ @sanitized_path ||= {}
38
+ @sanitized_path[base_directory] ||= {}
39
+ @sanitized_path[base_directory][questionable_path] ||= begin
40
+ if questionable_path.nil?
41
+ base_directory.freeze
42
+ else
43
+ sanitize_and_join(base_directory, questionable_path).freeze
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def sanitize_and_join(base_directory, questionable_path)
51
+ clean_path = if questionable_path.start_with?("~")
52
+ questionable_path.dup.insert(0, "/")
53
+ else
54
+ questionable_path
55
+ end
56
+ clean_path = File.expand_path(clean_path, "/")
57
+ return clean_path if clean_path.eql?(base_directory)
58
+
59
+ # remove any remaining extra leading slashes not stripped away by calling
60
+ # `File.expand_path` above.
61
+ clean_path.squeeze!("/")
62
+ return clean_path if clean_path.start_with?(slashed_dir_cache(base_directory))
63
+
64
+ clean_path.sub!(%r!\A\w:/!, "/")
65
+ join(base_directory, clean_path)
66
+ end
67
+
68
+ def slashed_dir_cache(base_directory)
69
+ @slashed_dir_cache ||= {}
70
+ @slashed_dir_cache[base_directory] ||= base_directory.sub(%r!\z!, "/")
71
+ end
29
72
  end
30
73
  end
31
74
  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
@@ -164,8 +164,6 @@ module Jekyll
164
164
  entry_filter = EntryFilter.new(site)
165
165
 
166
166
  site.include.each do |entry|
167
- next if entry == ".htaccess"
168
-
169
167
  entry_path = site.in_source_dir(entry)
170
168
  next if File.directory?(entry_path)
171
169
  next if entry_filter.symlink?(entry_path)
@@ -175,13 +173,20 @@ module Jekyll
175
173
  end
176
174
 
177
175
  def read_included_file(entry_path)
178
- dir = File.dirname(entry_path).sub(site.source, "")
179
- file = Array(File.basename(entry_path))
180
176
  if Utils.has_yaml_header?(entry_path)
181
- site.pages.concat(PageReader.new(site, dir).read(file))
177
+ conditionally_generate_entry(entry_path, site.pages, PageReader)
182
178
  else
183
- site.static_files.concat(StaticFileReader.new(site, dir).read(file))
179
+ conditionally_generate_entry(entry_path, site.static_files, StaticFileReader)
184
180
  end
185
181
  end
182
+
183
+ def conditionally_generate_entry(entry_path, container, reader)
184
+ return if container.find { |item| site.in_source_dir(item.relative_path) == entry_path }
185
+
186
+ dir, files = File.split(entry_path)
187
+ dir.sub!(site.source, "")
188
+ files = Array(files)
189
+ container.concat(reader.new(site, dir).read(files))
190
+ end
186
191
  end
187
192
  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,10 +3,12 @@
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 = {}
9
10
  @entry_filter = EntryFilter.new(site)
11
+ @source_dir = site.in_source_dir("/")
10
12
  end
11
13
 
12
14
  # Read all the files in <dir> and adds them to @content
@@ -52,6 +54,8 @@ module Jekyll
52
54
  #
53
55
  # Returns the contents of the data file.
54
56
  def read_data_file(path)
57
+ Jekyll.logger.debug "Reading:", path.sub(@source_dir, "")
58
+
55
59
  case File.extname(path).downcase
56
60
  when ".csv"
57
61
  CSV.read(path,
@@ -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
@@ -56,7 +57,7 @@ module Jekyll
56
57
  Document.new(path,
57
58
  :site => @site,
58
59
  :collection => @site.posts)
59
- end.reject(&:nil?)
60
+ end.tap(&:compact!)
60
61
  end
61
62
 
62
63
  private
@@ -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
@@ -46,7 +46,7 @@ module Jekyll
46
46
  end
47
47
 
48
48
  def most_recent_posts
49
- @most_recent_posts ||= (site.posts.docs.last(11).reverse - [post]).first(10)
49
+ @most_recent_posts ||= (site.posts.docs.last(11).reverse! - [post]).first(10)
50
50
  end
51
51
  end
52
52
  end
@@ -36,7 +36,7 @@ module Jekyll
36
36
  #
37
37
  # Returns Array of Converter instances.
38
38
  def converters
39
- @converters ||= site.converters.select { |c| c.matches(document.extname) }.sort
39
+ @converters ||= site.converters.select { |c| c.matches(document.extname) }.tap(&:sort!)
40
40
  end
41
41
 
42
42
  # Determine the extname the outputted file should have
@@ -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, Metrics/MethodLength
70
70
  def render_document
71
71
  info = {
72
72
  :registers => { :site => site, :page => payload["page"] },
@@ -84,6 +84,10 @@ module Jekyll
84
84
  output = convert(output.to_s)
85
85
  document.content = output
86
86
 
87
+ Jekyll.logger.debug "Post-Convert Hooks:", document.relative_path
88
+ document.trigger_hooks(:post_convert)
89
+ output = document.content
90
+
87
91
  if document.place_in_layout?
88
92
  Jekyll.logger.debug "Rendering Layout:", document.relative_path
89
93
  output = place_in_layouts(output, payload, info)
@@ -91,7 +95,7 @@ module Jekyll
91
95
 
92
96
  output
93
97
  end
94
- # rubocop: enable AbcSize
98
+ # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
95
99
 
96
100
  # Convert the document using the converters which match this renderer's document.
97
101
  #
@@ -125,13 +129,13 @@ module Jekyll
125
129
  LiquidRenderer.format_error(e, path || document.relative_path)
126
130
  end
127
131
  template.render!(payload, info)
128
- # rubocop: disable RescueException
132
+ # rubocop: disable Lint/RescueException
129
133
  rescue Exception => e
130
134
  Jekyll.logger.error "Liquid Exception:",
131
135
  LiquidRenderer.format_error(e, path || document.relative_path)
132
136
  raise e
133
137
  end
134
- # rubocop: enable RescueException
138
+ # rubocop: enable Lint/RescueException
135
139
 
136
140
  # Checks if the layout specified in the document actually exists
137
141
  #
@@ -174,16 +178,10 @@ module Jekyll
174
178
  # layout - the layout to check
175
179
  # Returns nothing
176
180
  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
181
+ return unless invalid_layout?(layout)
182
+
183
+ Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
184
+ "in #{document.relative_path} does not exist."
187
185
  end
188
186
 
189
187
  # Render layout content into document.output
@@ -197,7 +195,7 @@ module Jekyll
197
195
  layout.content,
198
196
  payload,
199
197
  info,
200
- layout.relative_path
198
+ layout.path
201
199
  )
202
200
  end
203
201
 
@@ -257,7 +255,7 @@ module Jekyll
257
255
  def output_exts
258
256
  @output_exts ||= converters.map do |c|
259
257
  c.output_ext(document.extname)
260
- end.compact
258
+ end.tap(&:compact!)
261
259
  end
262
260
 
263
261
  def liquid_options
@@ -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 = {}
@@ -112,8 +117,10 @@ module Jekyll
112
117
 
113
118
  Jekyll::Cache.clear_if_config_changed config
114
119
  Jekyll::Hooks.trigger :site, :after_reset, self
120
+ nil
115
121
  end
116
122
  # rubocop:enable Metrics/MethodLength
123
+ # rubocop:enable Metrics/AbcSize
117
124
 
118
125
  # Load necessary libraries, plugins, converters, and generators.
119
126
  #
@@ -145,9 +152,9 @@ module Jekyll
145
152
  #
146
153
  # Returns a Hash containing collection name-to-instance pairs.
147
154
  def collections
148
- @collections ||= Hash[collection_names.map do |coll|
149
- [coll, Jekyll::Collection.new(self, coll)]
150
- end]
155
+ @collections ||= collection_names.each_with_object({}) do |name, hsh|
156
+ hsh[name] = Jekyll::Collection.new(self, name)
157
+ end
151
158
  end
152
159
 
153
160
  # The list of collection names.
@@ -174,6 +181,7 @@ module Jekyll
174
181
  reader.read
175
182
  limit_posts!
176
183
  Jekyll::Hooks.trigger :site, :post_read, self
184
+ nil
177
185
  end
178
186
 
179
187
  # Run each of the Generators.
@@ -186,6 +194,7 @@ module Jekyll
186
194
  Jekyll.logger.debug "Generating:",
187
195
  "#{generator.class} finished in #{Time.now - start} seconds."
188
196
  end
197
+ nil
189
198
  end
190
199
 
191
200
  # Render the site to the destination.
@@ -202,6 +211,7 @@ module Jekyll
202
211
  render_pages(payload)
203
212
 
204
213
  Jekyll::Hooks.trigger :site, :post_render, self, payload
214
+ nil
205
215
  end
206
216
 
207
217
  # Remove orphaned files and empty directories in destination.
@@ -209,17 +219,20 @@ module Jekyll
209
219
  # Returns nothing.
210
220
  def cleanup
211
221
  site_cleaner.cleanup!
222
+ nil
212
223
  end
213
224
 
214
225
  # Write static files, pages, and posts.
215
226
  #
216
227
  # Returns nothing.
217
228
  def write
229
+ Jekyll::Commands::Doctor.conflicting_urls(self)
218
230
  each_site_file do |item|
219
231
  item.write(dest) if regenerator.regenerate?(item)
220
232
  end
221
233
  regenerator.write_metadata
222
234
  Jekyll::Hooks.trigger :site, :post_write, self
235
+ nil
223
236
  end
224
237
 
225
238
  def posts
@@ -303,8 +316,9 @@ module Jekyll
303
316
  # passed in as argument.
304
317
 
305
318
  def instantiate_subclasses(klass)
306
- klass.descendants.select { |c| !safe || c.safe }.sort.map do |c|
307
- c.new(config)
319
+ klass.descendants.select { |c| !safe || c.safe }.tap do |result|
320
+ result.sort!
321
+ result.map! { |c| c.new(config) }
308
322
  end
309
323
  end
310
324
 
@@ -428,9 +442,18 @@ module Jekyll
428
442
  @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
429
443
  end
430
444
 
445
+ # Public
446
+ #
447
+ # Returns the object as a debug String.
448
+ def inspect
449
+ "#<#{self.class} @source=#{@source}>"
450
+ end
451
+
431
452
  private
432
453
 
433
454
  def load_theme_configuration(config)
455
+ return config if config["ignore_theme_config"] == true
456
+
434
457
  theme_config_file = in_theme_dir("_config.yml")
435
458
  return config unless File.exist?(theme_config_file)
436
459
 
@@ -470,7 +493,7 @@ module Jekyll
470
493
  # Disable Marshaling cache to disk in Safe Mode
471
494
  def configure_cache
472
495
  Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
473
- Jekyll::Cache.disable_disk_cache! if safe
496
+ Jekyll::Cache.disable_disk_cache! if safe || config["disable_disk_cache"]
474
497
  end
475
498
 
476
499
  def configure_plugins
@@ -520,7 +543,8 @@ module Jekyll
520
543
  def render_regenerated(document, payload)
521
544
  return unless regenerator.regenerate?(document)
522
545
 
523
- document.output = Jekyll::Renderer.new(self, document, payload).run
546
+ document.renderer.payload = payload
547
+ document.output = document.renderer.run
524
548
  document.trigger_hooks(:post_render)
525
549
  end
526
550
  end