jekyll 3.8.7 → 4.1.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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +71 -62
  3. data/LICENSE +1 -1
  4. data/README.markdown +46 -17
  5. data/lib/blank_template/_config.yml +3 -0
  6. data/lib/blank_template/_layouts/default.html +12 -0
  7. data/lib/blank_template/_sass/main.scss +9 -0
  8. data/lib/blank_template/assets/css/main.scss +4 -0
  9. data/lib/blank_template/index.md +8 -0
  10. data/lib/jekyll.rb +10 -1
  11. data/lib/jekyll/cache.rb +190 -0
  12. data/lib/jekyll/cleaner.rb +5 -4
  13. data/lib/jekyll/collection.rb +82 -10
  14. data/lib/jekyll/command.rb +33 -6
  15. data/lib/jekyll/commands/build.rb +11 -20
  16. data/lib/jekyll/commands/clean.rb +2 -0
  17. data/lib/jekyll/commands/doctor.rb +15 -8
  18. data/lib/jekyll/commands/help.rb +1 -1
  19. data/lib/jekyll/commands/new.rb +37 -35
  20. data/lib/jekyll/commands/new_theme.rb +30 -28
  21. data/lib/jekyll/commands/serve.rb +55 -81
  22. data/lib/jekyll/commands/serve/live_reload_reactor.rb +6 -10
  23. data/lib/jekyll/commands/serve/servlet.rb +22 -25
  24. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  25. data/lib/jekyll/configuration.rb +61 -149
  26. data/lib/jekyll/converters/identity.rb +18 -0
  27. data/lib/jekyll/converters/markdown.rb +49 -40
  28. data/lib/jekyll/converters/markdown/kramdown_parser.rb +84 -11
  29. data/lib/jekyll/converters/smartypants.rb +34 -14
  30. data/lib/jekyll/convertible.rb +30 -31
  31. data/lib/jekyll/deprecator.rb +1 -3
  32. data/lib/jekyll/document.rb +89 -61
  33. data/lib/jekyll/drops/collection_drop.rb +2 -3
  34. data/lib/jekyll/drops/document_drop.rb +14 -1
  35. data/lib/jekyll/drops/drop.rb +17 -14
  36. data/lib/jekyll/drops/excerpt_drop.rb +4 -0
  37. data/lib/jekyll/drops/page_drop.rb +18 -0
  38. data/lib/jekyll/drops/site_drop.rb +6 -5
  39. data/lib/jekyll/drops/unified_payload_drop.rb +1 -0
  40. data/lib/jekyll/drops/url_drop.rb +53 -1
  41. data/lib/jekyll/entry_filter.rb +42 -45
  42. data/lib/jekyll/excerpt.rb +45 -34
  43. data/lib/jekyll/external.rb +10 -5
  44. data/lib/jekyll/filters.rb +200 -40
  45. data/lib/jekyll/filters/date_filters.rb +6 -3
  46. data/lib/jekyll/filters/grouping_filters.rb +1 -2
  47. data/lib/jekyll/filters/url_filters.rb +46 -14
  48. data/lib/jekyll/frontmatter_defaults.rb +46 -35
  49. data/lib/jekyll/hooks.rb +4 -8
  50. data/lib/jekyll/inclusion.rb +32 -0
  51. data/lib/jekyll/liquid_extensions.rb +0 -2
  52. data/lib/jekyll/liquid_renderer.rb +31 -16
  53. data/lib/jekyll/liquid_renderer/file.rb +24 -3
  54. data/lib/jekyll/liquid_renderer/table.rb +36 -77
  55. data/lib/jekyll/log_adapter.rb +5 -1
  56. data/lib/jekyll/mime.types +53 -11
  57. data/lib/jekyll/page.rb +54 -12
  58. data/lib/jekyll/page_excerpt.rb +26 -0
  59. data/lib/jekyll/page_without_a_file.rb +0 -4
  60. data/lib/jekyll/path_manager.rb +31 -0
  61. data/lib/jekyll/plugin.rb +5 -11
  62. data/lib/jekyll/plugin_manager.rb +2 -0
  63. data/lib/jekyll/profiler.rb +58 -0
  64. data/lib/jekyll/reader.rb +42 -9
  65. data/lib/jekyll/readers/collection_reader.rb +1 -0
  66. data/lib/jekyll/readers/data_reader.rb +8 -9
  67. data/lib/jekyll/readers/layout_reader.rb +3 -12
  68. data/lib/jekyll/readers/page_reader.rb +5 -5
  69. data/lib/jekyll/readers/post_reader.rb +31 -18
  70. data/lib/jekyll/readers/static_file_reader.rb +4 -4
  71. data/lib/jekyll/readers/theme_assets_reader.rb +8 -5
  72. data/lib/jekyll/regenerator.rb +4 -12
  73. data/lib/jekyll/renderer.rb +23 -40
  74. data/lib/jekyll/site.rb +91 -38
  75. data/lib/jekyll/static_file.rb +62 -21
  76. data/lib/jekyll/stevenson.rb +2 -3
  77. data/lib/jekyll/tags/highlight.rb +19 -51
  78. data/lib/jekyll/tags/include.rb +82 -42
  79. data/lib/jekyll/tags/link.rb +11 -7
  80. data/lib/jekyll/tags/post_url.rb +25 -21
  81. data/lib/jekyll/theme.rb +16 -18
  82. data/lib/jekyll/theme_builder.rb +91 -89
  83. data/lib/jekyll/url.rb +10 -5
  84. data/lib/jekyll/utils.rb +18 -21
  85. data/lib/jekyll/utils/ansi.rb +1 -1
  86. data/lib/jekyll/utils/exec.rb +0 -1
  87. data/lib/jekyll/utils/internet.rb +2 -4
  88. data/lib/jekyll/utils/platforms.rb +8 -8
  89. data/lib/jekyll/utils/thread_event.rb +1 -5
  90. data/lib/jekyll/utils/win_tz.rb +2 -2
  91. data/lib/jekyll/version.rb +1 -1
  92. data/lib/site_template/.gitignore +2 -0
  93. data/lib/site_template/404.html +1 -0
  94. data/lib/site_template/_config.yml +17 -5
  95. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
  96. data/lib/site_template/{about.md → about.markdown} +0 -0
  97. data/lib/site_template/{index.md → index.markdown} +0 -0
  98. data/lib/theme_template/gitignore.erb +1 -0
  99. data/lib/theme_template/theme.gemspec.erb +1 -4
  100. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -0
  101. metadata +69 -31
  102. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
  103. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
  104. data/lib/jekyll/utils/rouge.rb +0 -22
@@ -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
@@ -10,9 +10,5 @@ module Jekyll
10
10
  def read_yaml(*)
11
11
  @data ||= {}
12
12
  end
13
-
14
- def inspect
15
- "#<Jekyll:PageWithoutAFile @name=#{name.inspect}>"
16
- end
17
13
  end
18
14
  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
@@ -13,7 +13,7 @@ module Jekyll
13
13
  #
14
14
 
15
15
  def self.inherited(const)
16
- return catch_inheritance(const) do |const_|
16
+ catch_inheritance(const) do |const_|
17
17
  catch_inheritance(const_)
18
18
  end
19
19
  end
@@ -23,9 +23,7 @@ module Jekyll
23
23
  def self.catch_inheritance(const)
24
24
  const.define_singleton_method :inherited do |const_|
25
25
  (@children ||= Set.new).add const_
26
- if block_given?
27
- yield const_
28
- end
26
+ yield const_ if block_given?
29
27
  end
30
28
  end
31
29
 
@@ -48,9 +46,7 @@ module Jekyll
48
46
  # Returns the Symbol priority.
49
47
  def self.priority(priority = nil)
50
48
  @priority ||= nil
51
- if priority && PRIORITIES.key?(priority)
52
- @priority = priority
53
- end
49
+ @priority = priority if priority && PRIORITIES.key?(priority)
54
50
  @priority || :normal
55
51
  end
56
52
 
@@ -62,9 +58,7 @@ module Jekyll
62
58
  #
63
59
  # Returns the safety Boolean.
64
60
  def self.safe(safe = nil)
65
- unless defined?(@safe) && safe.nil?
66
- @safe = safe
67
- end
61
+ @safe = safe unless defined?(@safe) && safe.nil?
68
62
  @safe || false
69
63
  end
70
64
 
@@ -74,7 +68,7 @@ module Jekyll
74
68
  #
75
69
  # Returns -1, 0, 1.
76
70
  def self.<=>(other)
77
- PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
71
+ PRIORITIES[other.priority] <=> PRIORITIES[priority]
78
72
  end
79
73
 
80
74
  # Spaceship is priority [higher -> lower]
@@ -37,8 +37,10 @@ module Jekyll
37
37
  # Returns false only if no dependencies have been specified, otherwise nothing.
38
38
  def require_theme_deps
39
39
  return false unless site.theme.runtime_dependencies
40
+
40
41
  site.theme.runtime_dependencies.each do |dep|
41
42
  next if dep.name == "jekyll"
43
+
42
44
  External.require_with_graceful_fail(dep.name) if plugin_allowed?(dep.name)
43
45
  end
44
46
  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
@@ -14,6 +14,7 @@ module Jekyll
14
14
  def read
15
15
  @site.layouts = LayoutReader.new(site).read
16
16
  read_directories
17
+ read_included_excludes
17
18
  sort_files!
18
19
  @site.data = DataReader.new(site).read(site.config["data_dir"])
19
20
  CollectionReader.new(site).read
@@ -39,13 +40,21 @@ module Jekyll
39
40
 
40
41
  return unless File.directory?(base)
41
42
 
43
+ dot_dirs = []
44
+ dot_pages = []
45
+ dot_static_files = []
46
+
42
47
  dot = Dir.chdir(base) { filter_entries(Dir.entries("."), base) }
43
- dot_dirs = dot.select { |file| File.directory?(@site.in_source_dir(base, file)) }
44
- dot_files = (dot - dot_dirs)
45
- dot_pages = dot_files.select do |file|
46
- Utils.has_yaml_header?(@site.in_source_dir(base, file))
48
+ dot.each do |entry|
49
+ file_path = @site.in_source_dir(base, entry)
50
+ if File.directory?(file_path)
51
+ dot_dirs << entry
52
+ elsif Utils.has_yaml_header?(file_path)
53
+ dot_pages << entry
54
+ else
55
+ dot_static_files << entry
56
+ end
47
57
  end
48
- dot_static_files = dot_files - dot_pages
49
58
 
50
59
  retrieve_posts(dir)
51
60
  retrieve_dirs(base, dir, dot_dirs)
@@ -61,6 +70,7 @@ module Jekyll
61
70
  # Returns nothing.
62
71
  def retrieve_posts(dir)
63
72
  return if outside_configured_directory?(dir)
73
+
64
74
  site.posts.docs.concat(post_reader.read_posts(dir))
65
75
  site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts
66
76
  end
@@ -75,10 +85,8 @@ module Jekyll
75
85
  def retrieve_dirs(_base, dir, dot_dirs)
76
86
  dot_dirs.each do |file|
77
87
  dir_path = site.in_source_dir(dir, file)
78
- rel_path = File.join(dir, file)
79
- unless @site.dest.chomp("/") == dir_path
80
- @site.reader.read_directories(rel_path)
81
- end
88
+ rel_path = PathManager.join(dir, file)
89
+ @site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
82
90
  end
83
91
  end
84
92
 
@@ -126,6 +134,7 @@ module Jekyll
126
134
  def get_entries(dir, subfolder)
127
135
  base = site.in_source_dir(dir, subfolder)
128
136
  return [] unless File.exist?(base)
137
+
129
138
  entries = Dir.chdir(base) { filter_entries(Dir["**/*"], base) }
130
139
  entries.delete_if { |e| File.directory?(site.in_source_dir(base, e)) }
131
140
  end
@@ -150,5 +159,29 @@ module Jekyll
150
159
  def post_reader
151
160
  @post_reader ||= PostReader.new(site)
152
161
  end
162
+
163
+ def read_included_excludes
164
+ entry_filter = EntryFilter.new(site)
165
+
166
+ site.include.each do |entry|
167
+ next if entry == ".htaccess"
168
+
169
+ entry_path = site.in_source_dir(entry)
170
+ next if File.directory?(entry_path)
171
+ next if entry_filter.symlink?(entry_path)
172
+
173
+ read_included_file(entry_path) if File.file?(entry_path)
174
+ end
175
+ end
176
+
177
+ def read_included_file(entry_path)
178
+ dir = File.dirname(entry_path).sub(site.source, "")
179
+ file = Array(File.basename(entry_path))
180
+ if Utils.has_yaml_header?(entry_path)
181
+ site.pages.concat(PageReader.new(site, dir).read(file))
182
+ else
183
+ site.static_files.concat(StaticFileReader.new(site, dir).read(file))
184
+ end
185
+ end
153
186
  end
154
187
  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 = {}
@@ -54,16 +55,14 @@ module Jekyll
54
55
  def read_data_file(path)
55
56
  case File.extname(path).downcase
56
57
  when ".csv"
57
- CSV.read(path, {
58
- :headers => true,
59
- :encoding => site.config["encoding"],
60
- }).map(&:to_hash)
58
+ CSV.read(path,
59
+ :headers => true,
60
+ :encoding => site.config["encoding"]).map(&:to_hash)
61
61
  when ".tsv"
62
- CSV.read(path, {
63
- :col_sep => "\t",
64
- :headers => true,
65
- :encoding => site.config["encoding"],
66
- }).map(&:to_hash)
62
+ CSV.read(path,
63
+ :col_sep => "\t",
64
+ :headers => true,
65
+ :encoding => site.config["encoding"]).map(&:to_hash)
67
66
  else
68
67
  SafeYAML.load_file(path)
69
68
  end
@@ -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 = {}
@@ -23,7 +24,7 @@ module Jekyll
23
24
  end
24
25
 
25
26
  def layout_directory
26
- @layout_directory ||= (layout_directory_in_cwd || layout_directory_inside_source)
27
+ @layout_directory ||= site.in_source_dir(site.config["layouts_dir"])
27
28
  end
28
29
 
29
30
  def theme_layout_directory
@@ -54,18 +55,8 @@ module Jekyll
54
55
 
55
56
  def within(directory)
56
57
  return unless File.exist?(directory)
57
- Dir.chdir(directory) { yield }
58
- end
59
58
 
60
- def layout_directory_inside_source
61
- site.in_source_dir(site.config["layouts_dir"])
62
- end
63
-
64
- def layout_directory_in_cwd
65
- dir = Jekyll.sanitized_path(Dir.pwd, site.config["layouts_dir"])
66
- if File.directory?(dir) && !site.safe
67
- dir
68
- end
59
+ Dir.chdir(directory) { yield }
69
60
  end
70
61
  end
71
62
  end
@@ -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
@@ -34,19 +35,9 @@ module Jekyll
34
35
  #
35
36
  # Returns nothing.
36
37
  def read_publishable(dir, magic_dir, matcher)
37
- read_content(dir, magic_dir, matcher).tap { |docs| docs.each(&:read) }
38
- .select do |doc|
39
- if doc.content.valid_encoding?
40
- site.publisher.publish?(doc).tap do |will_publish|
41
- if !will_publish && site.publisher.hidden_in_the_future?(doc)
42
- Jekyll.logger.debug "Skipping:", "#{doc.relative_path} has a future date"
43
- end
44
- end
45
- else
46
- Jekyll.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8"
47
- false
48
- end
49
- end
38
+ read_content(dir, magic_dir, matcher)
39
+ .tap { |docs| docs.each(&:read) }
40
+ .select { |doc| processable?(doc) }
50
41
  end
51
42
 
52
43
  # Read all the content files from <source>/<dir>/magic_dir
@@ -60,13 +51,35 @@ module Jekyll
60
51
  # Returns klass type of content files
61
52
  def read_content(dir, magic_dir, matcher)
62
53
  @site.reader.get_entries(dir, magic_dir).map do |entry|
63
- next unless entry =~ matcher
54
+ next unless matcher.match?(entry)
55
+
64
56
  path = @site.in_source_dir(File.join(dir, magic_dir, entry))
65
- Document.new(path, {
66
- :site => @site,
67
- :collection => @site.posts,
68
- })
57
+ Document.new(path,
58
+ :site => @site,
59
+ :collection => @site.posts)
69
60
  end.reject(&:nil?)
70
61
  end
62
+
63
+ private
64
+
65
+ def processable?(doc)
66
+ if doc.content.nil?
67
+ Jekyll.logger.debug "Skipping:", "Content in #{doc.relative_path} is nil"
68
+ false
69
+ elsif !doc.content.valid_encoding?
70
+ Jekyll.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8"
71
+ false
72
+ else
73
+ publishable?(doc)
74
+ end
75
+ end
76
+
77
+ def publishable?(doc)
78
+ site.publisher.publish?(doc).tap do |will_publish|
79
+ if !will_publish && site.publisher.hidden_in_the_future?(doc)
80
+ Jekyll.logger.warn "Skipping:", "#{doc.relative_path} has a future date"
81
+ end
82
+ end
83
+ end
71
84
  end
72
85
  end