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
@@ -7,9 +7,8 @@ module Jekyll
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :write?, :output
11
- def_delegators :@obj, :label, :docs, :files, :directory,
12
- :relative_directory
10
+ def_delegator :@obj, :write?, :output
11
+ def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory
13
12
 
14
13
  private def_delegator :@obj, :metadata, :fallback_data
15
14
 
@@ -12,7 +12,7 @@ module Jekyll
12
12
  mutable false
13
13
 
14
14
  def_delegator :@obj, :relative_path, :path
15
- def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
15
+ def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url, :date
16
16
 
17
17
  private def_delegator :@obj, :data, :fallback_data
18
18
 
@@ -26,6 +26,7 @@ module Jekyll
26
26
 
27
27
  def <=>(other)
28
28
  return nil unless other.is_a? DocumentDrop
29
+
29
30
  cmp = self["date"] <=> other["date"]
30
31
  cmp = self["path"] <=> other["path"] if cmp.nil? || cmp.zero?
31
32
  cmp
@@ -63,6 +64,18 @@ module Jekyll
63
64
  result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
64
65
  end
65
66
  end
67
+
68
+ def title
69
+ @obj.data["title"]
70
+ end
71
+
72
+ def categories
73
+ @obj.data["categories"]
74
+ end
75
+
76
+ def tags
77
+ @obj.data["tags"]
78
+ end
66
79
  end
67
80
  end
68
81
  end
@@ -15,11 +15,7 @@ module Jekyll
15
15
  #
16
16
  # Returns the mutability of the class
17
17
  def self.mutable(is_mutable = nil)
18
- @is_mutable = if is_mutable
19
- is_mutable
20
- else
21
- false
22
- end
18
+ @is_mutable = is_mutable || false
23
19
  end
24
20
 
25
21
  def self.mutable?
@@ -34,7 +30,6 @@ module Jekyll
34
30
  # Returns nothing
35
31
  def initialize(obj)
36
32
  @obj = obj
37
- @mutations = {} # only if mutable: true
38
33
  end
39
34
 
40
35
  # Access a method in the Drop or a field in the underlying hash data.
@@ -46,8 +41,8 @@ module Jekyll
46
41
  #
47
42
  # Returns the value for the given key, or nil if none exists
48
43
  def [](key)
49
- if self.class.mutable? && @mutations.key?(key)
50
- @mutations[key]
44
+ if self.class.mutable? && mutations.key?(key)
45
+ mutations[key]
51
46
  elsif self.class.invokable? key
52
47
  public_send key
53
48
  else
@@ -70,11 +65,12 @@ module Jekyll
70
65
  # and the key matches a method in which case it raises a
71
66
  # DropMutationException.
72
67
  def []=(key, val)
73
- if respond_to?("#{key}=")
74
- public_send("#{key}=", val)
68
+ setter = "#{key}="
69
+ if respond_to?(setter)
70
+ public_send(setter, val)
75
71
  elsif respond_to?(key.to_s)
76
72
  if self.class.mutable?
77
- @mutations[key] = val
73
+ mutations[key] = val
78
74
  else
79
75
  raise Errors::DropMutationException, "Key #{key} cannot be set in the drop."
80
76
  end
@@ -104,7 +100,8 @@ module Jekyll
104
100
  # Returns true if the given key is present
105
101
  def key?(key)
106
102
  return false if key.nil?
107
- return true if self.class.mutable? && @mutations.key?(key)
103
+ return true if self.class.mutable? && mutations.key?(key)
104
+
108
105
  respond_to?(key) || fallback_data.key?(key)
109
106
  end
110
107
 
@@ -116,7 +113,7 @@ module Jekyll
116
113
  # Returns an Array of unique keys for content for the Drop.
117
114
  def keys
118
115
  (content_methods |
119
- @mutations.keys |
116
+ mutations.keys |
120
117
  fallback_data.keys).flatten
121
118
  end
122
119
 
@@ -173,7 +170,7 @@ module Jekyll
173
170
  end
174
171
 
175
172
  def merge(other, &block)
176
- self.dup.tap do |me|
173
+ dup.tap do |me|
177
174
  if block.nil?
178
175
  me.merge!(other)
179
176
  else
@@ -207,6 +204,12 @@ module Jekyll
207
204
  return yield(key) unless block.nil?
208
205
  return default unless default.nil?
209
206
  end
207
+
208
+ private
209
+
210
+ def mutations
211
+ @mutations ||= {}
212
+ end
210
213
  end
211
214
  end
212
215
  end
@@ -7,6 +7,10 @@ module Jekyll
7
7
  @obj.doc.data["layout"]
8
8
  end
9
9
 
10
+ def date
11
+ @obj.doc.date
12
+ end
13
+
10
14
  def excerpt
11
15
  nil
12
16
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Drops
5
+ class PageDrop < Drop
6
+ extend Forwardable
7
+
8
+ mutable false
9
+
10
+ def_delegators :@obj, :content, :dir, :name, :path, :url, :excerpt
11
+ private def_delegator :@obj, :data, :fallback_data
12
+
13
+ def title
14
+ @obj.data["title"]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -13,7 +13,7 @@ module Jekyll
13
13
  private def_delegator :@obj, :config, :fallback_data
14
14
 
15
15
  def [](key)
16
- if @obj.collections.key?(key) && key != "posts"
16
+ if key != "posts" && @obj.collections.key?(key)
17
17
  @obj.collections[key].docs
18
18
  else
19
19
  super(key)
@@ -21,7 +21,7 @@ module Jekyll
21
21
  end
22
22
 
23
23
  def key?(key)
24
- (@obj.collections.key?(key) && key != "posts") || super
24
+ (key != "posts" && @obj.collections.key?(key)) || super
25
25
  end
26
26
 
27
27
  def posts
@@ -41,9 +41,9 @@ module Jekyll
41
41
  # `Site#documents` cannot be memoized so that `Site#docs_to_write` can access the
42
42
  # latest state of the attribute.
43
43
  #
44
- # Since this method will be called after `Site#pre_render` hook,
45
- # the `Site#documents` array shouldn't thereafter change and can therefore be
46
- # safely memoized to prevent additional computation of `Site#documents`.
44
+ # Since this method will be called after `Site#pre_render` hook, the `Site#documents`
45
+ # array shouldn't thereafter change and can therefore be safely memoized to prevent
46
+ # additional computation of `Site#documents`.
47
47
  def documents
48
48
  @documents ||= @obj.documents
49
49
  end
@@ -54,6 +54,7 @@ module Jekyll
54
54
  # We should remove this in 4.0 and switch to `{{ post.related_posts }}`.
55
55
  def related_posts
56
56
  return nil unless @current_document.is_a?(Jekyll::Document)
57
+
57
58
  @current_document.related_posts
58
59
  end
59
60
  attr_writer :current_document
@@ -17,6 +17,7 @@ module Jekyll
17
17
  end
18
18
 
19
19
  private
20
+
20
21
  def fallback_data
21
22
  @fallback_data ||= {}
22
23
  end
@@ -35,53 +35,105 @@ module Jekyll
35
35
  category_set.to_a.join("/")
36
36
  end
37
37
 
38
+ # Similar to output from #categories, but each category will be downcased and
39
+ # all non-alphanumeric characters of the category replaced with a hyphen.
40
+ def slugified_categories
41
+ Array(@obj.data["categories"]).each_with_object(Set.new) do |category, set|
42
+ set << Utils.slugify(category.to_s)
43
+ end.to_a.join("/")
44
+ end
45
+
46
+ # CCYY
38
47
  def year
39
48
  @obj.date.strftime("%Y")
40
49
  end
41
50
 
51
+ # MM: 01..12
42
52
  def month
43
53
  @obj.date.strftime("%m")
44
54
  end
45
55
 
56
+ # DD: 01..31
46
57
  def day
47
58
  @obj.date.strftime("%d")
48
59
  end
49
60
 
61
+ # hh: 00..23
50
62
  def hour
51
63
  @obj.date.strftime("%H")
52
64
  end
53
65
 
66
+ # mm: 00..59
54
67
  def minute
55
68
  @obj.date.strftime("%M")
56
69
  end
57
70
 
71
+ # ss: 00..59
58
72
  def second
59
73
  @obj.date.strftime("%S")
60
74
  end
61
75
 
76
+ # D: 1..31
62
77
  def i_day
63
78
  @obj.date.strftime("%-d")
64
79
  end
65
80
 
81
+ # M: 1..12
66
82
  def i_month
67
83
  @obj.date.strftime("%-m")
68
84
  end
69
85
 
86
+ # MMM: Jan..Dec
70
87
  def short_month
71
88
  @obj.date.strftime("%b")
72
89
  end
73
90
 
91
+ # MMMM: January..December
92
+ def long_month
93
+ @obj.date.strftime("%B")
94
+ end
95
+
96
+ # YY: 00..99
74
97
  def short_year
75
98
  @obj.date.strftime("%y")
76
99
  end
77
100
 
101
+ # CCYYw, ISO week year
102
+ # may differ from CCYY for the first days of January and last days of December
103
+ def w_year
104
+ @obj.date.strftime("%G")
105
+ end
106
+
107
+ # WW: 01..53
108
+ # %W and %U do not comply with ISO 8601-1
109
+ def week
110
+ @obj.date.strftime("%V")
111
+ end
112
+
113
+ # d: 1..7 (Monday..Sunday)
114
+ def w_day
115
+ @obj.date.strftime("%u")
116
+ end
117
+
118
+ # dd: Mon..Sun
119
+ def short_day
120
+ @obj.date.strftime("%a")
121
+ end
122
+
123
+ # ddd: Monday..Sunday
124
+ def long_day
125
+ @obj.date.strftime("%A")
126
+ end
127
+
128
+ # DDD: 001..366
78
129
  def y_day
79
130
  @obj.date.strftime("%j")
80
131
  end
81
132
 
82
133
  private
134
+
83
135
  def fallback_data
84
- {}
136
+ @fallback_data ||= {}
85
137
  end
86
138
  end
87
139
  end
@@ -3,9 +3,8 @@
3
3
  module Jekyll
4
4
  class EntryFilter
5
5
  attr_reader :site
6
- SPECIAL_LEADING_CHARACTERS = [
7
- ".", "_", "#", "~",
8
- ].freeze
6
+
7
+ SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze
9
8
 
10
9
  def initialize(site, base_directory = nil)
11
10
  @site = site
@@ -29,16 +28,30 @@ module Jekyll
29
28
  )
30
29
  end
31
30
 
31
+ # rubocop:disable Metrics/CyclomaticComplexity
32
32
  def filter(entries)
33
33
  entries.reject do |e|
34
- # Reject this entry if it is a symlink.
34
+ # Reject this entry if it is just a "dot" representation.
35
+ # e.g.: '.', '..', '_movies/.', 'music/..', etc
36
+ next true if e.end_with?(".")
37
+
38
+ # Check if the current entry is explicitly included and cache the result
39
+ included = included?(e)
40
+
41
+ # Reject current entry if it is excluded but not explicitly included as well.
42
+ next true if excluded?(e) && !included
43
+
44
+ # Reject current entry if it is a symlink.
35
45
  next true if symlink?(e)
36
- # Do not reject this entry if it is included.
37
- next false if included?(e)
38
- # Reject this entry if it is special, a backup file, or excluded.
39
- special?(e) || backup?(e) || excluded?(e)
46
+
47
+ # Do not reject current entry if it is explicitly included.
48
+ next false if included
49
+
50
+ # Reject current entry if it is special or a backup file.
51
+ special?(e) || backup?(e)
40
52
  end
41
53
  end
54
+ # rubocop:enable Metrics/CyclomaticComplexity
42
55
 
43
56
  def included?(entry)
44
57
  glob_include?(site.include, entry) ||
@@ -46,16 +59,16 @@ module Jekyll
46
59
  end
47
60
 
48
61
  def special?(entry)
49
- SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
50
- SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
62
+ SPECIAL_LEADING_CHAR_REGEX.match?(entry) ||
63
+ SPECIAL_LEADING_CHAR_REGEX.match?(File.basename(entry))
51
64
  end
52
65
 
53
66
  def backup?(entry)
54
- entry[-1..-1] == "~"
67
+ entry.end_with?("~")
55
68
  end
56
69
 
57
70
  def excluded?(entry)
58
- glob_include?(site.exclude, relative_to_source(entry)).tap do |excluded|
71
+ glob_include?(site.exclude - site.include, relative_to_source(entry)).tap do |excluded|
59
72
  if excluded
60
73
  Jekyll.logger.debug(
61
74
  "EntryFilter:",
@@ -85,40 +98,24 @@ module Jekyll
85
98
  )
86
99
  end
87
100
 
88
- # --
89
- # Check if an entry matches a specific pattern and return true,false.
90
- # Returns true if path matches against any glob pattern.
91
- # --
92
- def glob_include?(enum, entry)
93
- entry_path = Pathutil.new(site.in_source_dir).join(entry)
94
- enum.any? do |exp|
95
- # Users who send a Regexp knows what they want to
96
- # exclude, so let them send a Regexp to exclude files,
97
- # we will not bother caring if it works or not, it's
98
- # on them at this point.
99
-
100
- if exp.is_a?(Regexp)
101
- entry_path =~ exp
102
-
101
+ # Check if an entry matches a specific pattern.
102
+ # Returns true if path matches against any glob pattern, else false.
103
+ def glob_include?(enumerator, entry)
104
+ entry_with_source = PathManager.join(site.source, entry)
105
+ entry_is_directory = File.directory?(entry_with_source)
106
+
107
+ enumerator.any? do |pattern|
108
+ case pattern
109
+ when String
110
+ pattern_with_source = PathManager.join(site.source, pattern)
111
+
112
+ File.fnmatch?(pattern_with_source, entry_with_source) ||
113
+ entry_with_source.start_with?(pattern_with_source) ||
114
+ (pattern_with_source == "#{entry_with_source}/" if entry_is_directory)
115
+ when Regexp
116
+ pattern.match?(entry_with_source)
103
117
  else
104
- item = Pathutil.new(site.in_source_dir).join(exp)
105
-
106
- # If it's a directory they want to exclude, AKA
107
- # ends with a "/" then we will go on to check and
108
- # see if the entry falls within that path and
109
- # exclude it if that's the case.
110
-
111
- if entry.end_with?("/")
112
- entry_path.in_path?(
113
- item
114
- )
115
-
116
- else
117
- File.fnmatch?(item, entry_path) ||
118
- entry_path.to_path.start_with?(
119
- item
120
- )
121
- end
118
+ false
122
119
  end
123
120
  end
124
121
  end
@@ -8,10 +8,11 @@ module Jekyll
8
8
  attr_accessor :content, :ext
9
9
  attr_writer :output
10
10
 
11
- def_delegators :@doc, :site, :name, :ext, :extname,
12
- :collection, :related_posts,
13
- :coffeescript_file?, :yaml_file?,
14
- :url, :next_doc, :previous_doc
11
+ def_delegators :@doc,
12
+ :site, :name, :ext, :extname,
13
+ :collection, :related_posts, :type,
14
+ :coffeescript_file?, :yaml_file?,
15
+ :url, :next_doc, :previous_doc
15
16
 
16
17
  private :coffeescript_file?, :yaml_file?
17
18
 
@@ -48,14 +49,14 @@ module Jekyll
48
49
  #
49
50
  # Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
50
51
  def relative_path
51
- File.join(doc.relative_path, "#excerpt")
52
+ @relative_path ||= File.join(doc.relative_path, "#excerpt")
52
53
  end
53
54
 
54
55
  # Check if excerpt includes a string
55
56
  #
56
57
  # Returns true if the string passed in
57
58
  def include?(something)
58
- (output && output.include?(something)) || content.include?(something)
59
+ output&.include?(something) || content.include?(something)
59
60
  end
60
61
 
61
62
  # The UID for this doc (useful in feeds).
@@ -76,7 +77,7 @@ module Jekyll
76
77
 
77
78
  # Returns the shorthand String identifier of this doc.
78
79
  def inspect
79
- "<Excerpt: #{self.id}>"
80
+ "<#{self.class} id=#{id}>"
80
81
  end
81
82
 
82
83
  def output
@@ -88,6 +89,8 @@ module Jekyll
88
89
  end
89
90
 
90
91
  def render_with_liquid?
92
+ return false if data["render_with_liquid"] == false
93
+
91
94
  !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
92
95
  end
93
96
 
@@ -128,36 +131,47 @@ module Jekyll
128
131
  #
129
132
  # Returns excerpt String
130
133
 
131
- LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m
132
- MKDWN_LINK_REF_REGEX = %r!^ {0,3}\[[^\]]+\]:.+$!
134
+ LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze
135
+ MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze
133
136
 
134
137
  def extract_excerpt(doc_content)
135
138
  head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
139
+ return head if tail.empty?
136
140
 
137
- # append appropriate closing tag(s) (for each Liquid block), to the `head`
138
- # if the partitioning resulted in leaving the closing tag somewhere
139
- # in the `tail` partition.
141
+ head = sanctify_liquid_tags(head) if head.include?("{%")
142
+ definitions = extract_markdown_link_reference_defintions(head, tail)
143
+ return head if definitions.empty?
140
144
 
141
- if head.include?("{%")
142
- modified = false
143
- tag_names = head.scan(LIQUID_TAG_REGEX)
144
- tag_names.flatten!
145
- tag_names.reverse_each do |tag_name|
146
- next unless liquid_block?(tag_name)
147
- next if head =~ endtag_regex_stash(tag_name)
145
+ head << "\n\n" << definitions.join("\n")
146
+ end
148
147
 
149
- modified = true
150
- head << "\n{% end#{tag_name} %}"
151
- end
152
- print_build_warning if modified
153
- end
148
+ private
154
149
 
155
- return head if tail.empty?
150
+ # append appropriate closing tag(s) (for each Liquid block), to the `head` if the
151
+ # partitioning resulted in leaving the closing tag somewhere in the `tail` partition.
152
+ def sanctify_liquid_tags(head)
153
+ modified = false
154
+ tag_names = head.scan(LIQUID_TAG_REGEX)
155
+ tag_names.flatten!
156
+ tag_names.reverse_each do |tag_name|
157
+ next unless liquid_block?(tag_name)
158
+ next if endtag_regex_stash(tag_name).match?(head)
159
+
160
+ modified = true
161
+ head << "\n{% end#{tag_name} %}"
162
+ end
156
163
 
157
- head << "\n\n" << tail.scan(MKDWN_LINK_REF_REGEX).join("\n")
164
+ print_build_warning if modified
165
+ head
158
166
  end
159
167
 
160
- private
168
+ def extract_markdown_link_reference_defintions(head, tail)
169
+ [].tap do |definitions|
170
+ tail.scan(MKDWN_LINK_REF_REGEX).each do |segments|
171
+ definitions << segments.join if head.include?(segments[0])
172
+ end
173
+ end
174
+ end
161
175
 
162
176
  def endtag_regex_stash(tag_name)
163
177
  @endtag_regex_stash ||= {}
@@ -171,8 +185,7 @@ module Jekyll
171
185
  Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block)
172
186
  rescue NoMethodError
173
187
  Jekyll.logger.error "Error:",
174
- "A Liquid tag in the excerpt of #{doc.relative_path} couldn't be " \
175
- "parsed."
188
+ "A Liquid tag in the excerpt of #{doc.relative_path} couldn't be parsed."
176
189
  raise
177
190
  end
178
191
 
@@ -180,11 +193,9 @@ module Jekyll
180
193
  Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
181
194
  Jekyll.logger.warn "", "Found a Liquid block containing the excerpt separator" \
182
195
  " #{doc.excerpt_separator.inspect}. "
183
- Jekyll.logger.warn "", "The block has been modified with the appropriate" \
184
- " closing tag."
185
- Jekyll.logger.warn "", "Feel free to define a custom excerpt or" \
186
- " excerpt_separator in the document's front matter" \
187
- " if the generated excerpt is unsatisfactory."
196
+ Jekyll.logger.warn "", "The block has been modified with the appropriate closing tag."
197
+ Jekyll.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the"
198
+ Jekyll.logger.warn "", "document's Front Matter if the generated excerpt is unsatisfactory."
188
199
  end
189
200
  end
190
201
  end