jekyll 4.0.0.pre.beta1 → 4.2.0

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