jekyll 3.8.7 → 4.1.0

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