jekyll 3.9.3 → 4.4.1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +511 -89
  3. data/LICENSE +1 -1
  4. data/README.markdown +48 -27
  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/base.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/cache.rb +186 -0
  11. data/lib/jekyll/cleaner.rb +8 -7
  12. data/lib/jekyll/collection.rb +84 -11
  13. data/lib/jekyll/command.rb +33 -6
  14. data/lib/jekyll/commands/build.rb +8 -28
  15. data/lib/jekyll/commands/clean.rb +3 -2
  16. data/lib/jekyll/commands/doctor.rb +46 -35
  17. data/lib/jekyll/commands/help.rb +1 -1
  18. data/lib/jekyll/commands/new.rb +44 -50
  19. data/lib/jekyll/commands/new_theme.rb +27 -28
  20. data/lib/jekyll/commands/serve/live_reload_reactor.rb +9 -16
  21. data/lib/jekyll/commands/serve/servlet.rb +21 -22
  22. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  23. data/lib/jekyll/commands/serve.rb +75 -97
  24. data/lib/jekyll/configuration.rb +66 -158
  25. data/lib/jekyll/converters/identity.rb +18 -0
  26. data/lib/jekyll/converters/markdown/kramdown_parser.rb +83 -33
  27. data/lib/jekyll/converters/markdown.rb +49 -40
  28. data/lib/jekyll/converters/smartypants.rb +34 -14
  29. data/lib/jekyll/convertible.rb +36 -34
  30. data/lib/jekyll/deprecator.rb +2 -4
  31. data/lib/jekyll/document.rb +107 -72
  32. data/lib/jekyll/drops/collection_drop.rb +3 -4
  33. data/lib/jekyll/drops/document_drop.rb +9 -3
  34. data/lib/jekyll/drops/drop.rb +115 -33
  35. data/lib/jekyll/drops/excerpt_drop.rb +8 -0
  36. data/lib/jekyll/drops/site_drop.rb +9 -8
  37. data/lib/jekyll/drops/static_file_drop.rb +4 -4
  38. data/lib/jekyll/drops/theme_drop.rb +39 -0
  39. data/lib/jekyll/drops/unified_payload_drop.rb +7 -2
  40. data/lib/jekyll/drops/url_drop.rb +55 -3
  41. data/lib/jekyll/entry_filter.rb +42 -51
  42. data/lib/jekyll/excerpt.rb +48 -38
  43. data/lib/jekyll/external.rb +20 -19
  44. data/lib/jekyll/filters/date_filters.rb +6 -3
  45. data/lib/jekyll/filters/grouping_filters.rb +1 -2
  46. data/lib/jekyll/filters/url_filters.rb +50 -15
  47. data/lib/jekyll/filters.rb +211 -50
  48. data/lib/jekyll/frontmatter_defaults.rb +45 -36
  49. data/lib/jekyll/hooks.rb +26 -26
  50. data/lib/jekyll/inclusion.rb +32 -0
  51. data/lib/jekyll/layout.rb +12 -19
  52. data/lib/jekyll/liquid_extensions.rb +0 -2
  53. data/lib/jekyll/liquid_renderer/file.rb +24 -3
  54. data/lib/jekyll/liquid_renderer/table.rb +26 -77
  55. data/lib/jekyll/liquid_renderer.rb +31 -16
  56. data/lib/jekyll/log_adapter.rb +5 -1
  57. data/lib/jekyll/page.rb +51 -23
  58. data/lib/jekyll/page_excerpt.rb +25 -0
  59. data/lib/jekyll/page_without_a_file.rb +0 -4
  60. data/lib/jekyll/path_manager.rb +74 -0
  61. data/lib/jekyll/plugin.rb +5 -11
  62. data/lib/jekyll/plugin_manager.rb +15 -5
  63. data/lib/jekyll/profiler.rb +51 -0
  64. data/lib/jekyll/reader.rb +65 -10
  65. data/lib/jekyll/readers/collection_reader.rb +1 -0
  66. data/lib/jekyll/readers/data_reader.rb +48 -10
  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 +32 -19
  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/related_posts.rb +1 -1
  74. data/lib/jekyll/renderer.rb +34 -49
  75. data/lib/jekyll/site.rb +151 -58
  76. data/lib/jekyll/static_file.rb +64 -28
  77. data/lib/jekyll/stevenson.rb +4 -8
  78. data/lib/jekyll/tags/highlight.rb +44 -57
  79. data/lib/jekyll/tags/include.rb +114 -80
  80. data/lib/jekyll/tags/link.rb +12 -7
  81. data/lib/jekyll/tags/post_url.rb +33 -30
  82. data/lib/jekyll/theme.rb +20 -18
  83. data/lib/jekyll/theme_builder.rb +91 -89
  84. data/lib/jekyll/url.rb +18 -10
  85. data/lib/jekyll/utils/ansi.rb +2 -2
  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 +37 -52
  89. data/lib/jekyll/utils/thread_event.rb +1 -5
  90. data/lib/jekyll/utils.rb +29 -28
  91. data/lib/jekyll/version.rb +1 -1
  92. data/lib/jekyll.rb +9 -14
  93. data/lib/site_template/.gitignore +2 -0
  94. data/lib/site_template/404.html +2 -1
  95. data/lib/site_template/_config.yml +17 -5
  96. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
  97. data/lib/theme_template/README.md.erb +1 -3
  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 +150 -0
  101. data/rubocop/jekyll/no_p_allowed.rb +5 -6
  102. data/rubocop/jekyll/no_puts_allowed.rb +5 -6
  103. metadata +149 -37
  104. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
  105. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
  106. data/lib/jekyll/utils/rouge.rb +0 -22
  107. /data/lib/site_template/{about.md → about.markdown} +0 -0
  108. /data/lib/site_template/{index.md → index.markdown} +0 -0
@@ -10,7 +10,7 @@ module Jekyll
10
10
  # forms: name, name=value, or name="<quoted list>"
11
11
  #
12
12
  # <quoted list> is a space-separated list of numbers
13
- SYNTAX = %r!^([a-zA-Z0-9.+#_-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!
13
+ SYNTAX = %r!^([a-zA-Z0-9.+#_-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$!.freeze
14
14
 
15
15
  def initialize(tag_name, markup, tokens)
16
16
  super
@@ -18,29 +18,31 @@ module Jekyll
18
18
  @lang = Regexp.last_match(1).downcase
19
19
  @highlight_options = parse_options(Regexp.last_match(2))
20
20
  else
21
- raise SyntaxError, <<-MSG
22
- Syntax Error in tag 'highlight' while parsing the following markup:
21
+ raise SyntaxError, <<~MSG
22
+ Syntax Error in tag 'highlight' while parsing the following markup:
23
23
 
24
- #{markup}
24
+ #{markup}
25
25
 
26
- Valid syntax: highlight <lang> [linenos]
27
- MSG
26
+ Valid syntax: highlight <lang> [linenos] [mark_lines="3 4 5"]
27
+
28
+ See https://jekyllrb.com/docs/liquid/tags/#code-snippet-highlighting for more details.
29
+ MSG
28
30
  end
29
31
  end
30
32
 
33
+ LEADING_OR_TRAILING_LINE_TERMINATORS = %r!\A(\n|\r)+|(\n|\r)+\z!.freeze
34
+
31
35
  def render(context)
32
36
  prefix = context["highlighter_prefix"] || ""
33
37
  suffix = context["highlighter_suffix"] || ""
34
- code = super.to_s.gsub(%r!\A(\n|\r)+|(\n|\r)+\z!, "")
35
-
36
- is_safe = !!context.registers[:site].safe
38
+ code = super.to_s.gsub(LEADING_OR_TRAILING_LINE_TERMINATORS, "")
37
39
 
38
40
  output =
39
41
  case context.registers[:site].highlighter
40
- when "pygments"
41
- render_pygments(code, is_safe)
42
42
  when "rouge"
43
43
  render_rouge(code)
44
+ when "pygments"
45
+ render_pygments(code, context)
44
46
  else
45
47
  render_codehighlighter(code)
46
48
  end
@@ -49,23 +51,9 @@ MSG
49
51
  prefix + rendered_output + suffix
50
52
  end
51
53
 
52
- def sanitized_opts(opts, is_safe)
53
- if is_safe
54
- Hash[[
55
- [:startinline, opts.fetch(:startinline, nil)],
56
- [:hl_lines, opts.fetch(:hl_lines, nil)],
57
- [:linenos, opts.fetch(:linenos, nil)],
58
- [:encoding, opts.fetch(:encoding, "utf-8")],
59
- [:cssclass, opts.fetch(:cssclass, nil)],
60
- ].reject { |f| f.last.nil? }]
61
- else
62
- opts
63
- end
64
- end
65
-
66
54
  private
67
55
 
68
- OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!
56
+ OPTIONS_REGEX = %r!(?:\w="[^"]*"|\w=\w|\w)+!.freeze
69
57
 
70
58
  def parse_options(input)
71
59
  options = {}
@@ -75,7 +63,7 @@ MSG
75
63
  input.scan(OPTIONS_REGEX) do |opt|
76
64
  key, value = opt.split("=")
77
65
  # If a quoted list, convert to array
78
- if value && value.include?('"')
66
+ if value&.include?('"')
79
67
  value.delete!('"')
80
68
  value = value.split
81
69
  end
@@ -86,41 +74,44 @@ MSG
86
74
  options
87
75
  end
88
76
 
89
- def render_pygments(code, is_safe)
90
- Jekyll::External.require_with_graceful_fail("pygments") unless defined?(Pygments)
77
+ def render_pygments(code, _context)
78
+ Jekyll.logger.warn "Warning:", "Highlight Tag no longer supports rendering with Pygments."
79
+ Jekyll.logger.warn "", "Using the default highlighter, Rouge, instead."
80
+ render_rouge(code)
81
+ end
91
82
 
92
- highlighted_code = Pygments.highlight(
93
- code,
94
- :lexer => @lang,
95
- :options => sanitized_opts(@highlight_options, is_safe)
96
- )
83
+ def render_rouge(code)
84
+ require "rouge"
85
+ formatter = Rouge::Formatters::HTML.new
86
+ formatter = line_highlighter_formatter(formatter) if @highlight_options[:mark_lines]
87
+ formatter = table_formatter(formatter) if @highlight_options[:linenos]
97
88
 
98
- if highlighted_code.nil?
99
- Jekyll.logger.error <<-MSG
100
- There was an error highlighting your code:
89
+ lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
90
+ formatter.format(lexer.lex(code))
91
+ end
101
92
 
102
- #{code}
93
+ def line_highlighter_formatter(formatter)
94
+ Rouge::Formatters::HTMLLineHighlighter.new(
95
+ formatter,
96
+ :highlight_lines => mark_lines
97
+ )
98
+ end
103
99
 
104
- While attempting to convert the above code, Pygments.rb returned an unacceptable value.
105
- This is usually a timeout problem solved by running `jekyll build` again.
106
- MSG
107
- raise ArgumentError, "Pygments.rb returned an unacceptable value "\
108
- "when attempting to highlight some code."
109
- end
100
+ def mark_lines
101
+ value = @highlight_options[:mark_lines]
102
+ return value.map(&:to_i) if value.is_a?(Array)
110
103
 
111
- highlighted_code.sub('<div class="highlight"><pre>', "").sub("</pre></div>", "")
104
+ raise SyntaxError, "Syntax Error for mark_lines declaration. Expected a " \
105
+ "double-quoted list of integers."
112
106
  end
113
107
 
114
- def render_rouge(code)
115
- formatter = Jekyll::Utils::Rouge.html_formatter(
116
- :line_numbers => @highlight_options[:linenos],
117
- :wrap => false,
108
+ def table_formatter(formatter)
109
+ Rouge::Formatters::HTMLTable.new(
110
+ formatter,
118
111
  :css_class => "highlight",
119
112
  :gutter_class => "gutter",
120
113
  :code_class => "code"
121
114
  )
122
- lexer = ::Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
123
- formatter.format(lexer.lex(code))
124
115
  end
125
116
 
126
117
  def render_codehighlighter(code)
@@ -128,12 +119,8 @@ MSG
128
119
  end
129
120
 
130
121
  def add_code_tag(code)
131
- code_attributes = [
132
- "class=\"language-#{@lang.to_s.tr("+", "-")}\"",
133
- "data-lang=\"#{@lang}\"",
134
- ].join(" ")
135
- "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
136
- "#{code.chomp}</code></pre></figure>"
122
+ code_attrs = %(class="language-#{@lang.tr("+", "-")}" data-lang="#{@lang}")
123
+ %(<figure class="highlight"><pre><code #{code_attrs}>#{code.chomp}</code></pre></figure>)
137
124
  end
138
125
  end
139
126
  end
@@ -2,37 +2,29 @@
2
2
 
3
3
  module Jekyll
4
4
  module Tags
5
- class IncludeTagError < StandardError
6
- attr_accessor :path
7
-
8
- def initialize(msg, path)
9
- super(msg)
10
- @path = path
11
- end
12
- end
13
-
14
5
  class IncludeTag < Liquid::Tag
15
6
  VALID_SYNTAX = %r!
16
7
  ([\w-]+)\s*=\s*
17
- (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
18
- !x
8
+ (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+))
9
+ !x.freeze
19
10
  VARIABLE_SYNTAX = %r!
20
- (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
11
+ (?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+)
21
12
  (?<params>.*)
22
- !mx
13
+ !mx.freeze
23
14
 
24
- FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
25
- VALID_FILENAME_CHARS = %r!^[\w/.\-()+~\#@]+$!
26
- INVALID_SEQUENCES = %r![./]{2,}!
15
+ FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!.freeze
16
+ VALID_FILENAME_CHARS = %r!^[\w/.\-()+~\#@]+$!.freeze
17
+ INVALID_SEQUENCES = %r![./]{2,}!.freeze
27
18
 
28
19
  def initialize(tag_name, markup, tokens)
29
20
  super
30
- matched = markup.strip.match(VARIABLE_SYNTAX)
21
+ markup = markup.strip
22
+ matched = markup.match(VARIABLE_SYNTAX)
31
23
  if matched
32
24
  @file = matched["variable"].strip
33
25
  @params = matched["params"].strip
34
26
  else
35
- @file, @params = markup.strip.split(%r!\s+!, 2)
27
+ @file, @params = markup.split(%r!\s+!, 2)
36
28
  end
37
29
  validate_params if @params
38
30
  @tag_name = tag_name
@@ -44,51 +36,47 @@ module Jekyll
44
36
 
45
37
  def parse_params(context)
46
38
  params = {}
47
- markup = @params
48
-
49
- while (match = VALID_SYNTAX.match(markup))
50
- markup = markup[match.end(0)..-1]
51
-
52
- value = if match[2]
53
- match[2].gsub(%r!\\"!, '"')
54
- elsif match[3]
55
- match[3].gsub(%r!\\'!, "'")
56
- elsif match[4]
57
- context[match[4]]
39
+ @params.scan(VALID_SYNTAX) do |key, d_quoted, s_quoted, variable|
40
+ value = if d_quoted
41
+ d_quoted.include?('\\"') ? d_quoted.gsub('\\"', '"') : d_quoted
42
+ elsif s_quoted
43
+ s_quoted.include?("\\'") ? s_quoted.gsub("\\'", "'") : s_quoted
44
+ elsif variable
45
+ context[variable]
58
46
  end
59
47
 
60
- params[match[1]] = value
48
+ params[key] = value
61
49
  end
62
50
  params
63
51
  end
64
52
 
65
53
  def validate_file_name(file)
66
- if file =~ INVALID_SEQUENCES || file !~ VALID_FILENAME_CHARS
67
- raise ArgumentError, <<-MSG
68
- Invalid syntax for include tag. File contains invalid characters or sequences:
54
+ if INVALID_SEQUENCES.match?(file) || !VALID_FILENAME_CHARS.match?(file)
55
+ raise ArgumentError, <<~MSG
56
+ Invalid syntax for include tag. File contains invalid characters or sequences:
69
57
 
70
- #{file}
58
+ #{file}
71
59
 
72
- Valid syntax:
60
+ Valid syntax:
73
61
 
74
- #{syntax_example}
62
+ #{syntax_example}
75
63
 
76
- MSG
64
+ MSG
77
65
  end
78
66
  end
79
67
 
80
68
  def validate_params
81
- unless @params =~ FULL_VALID_SYNTAX
82
- raise ArgumentError, <<-MSG
83
- Invalid syntax for include tag:
69
+ unless FULL_VALID_SYNTAX.match?(@params)
70
+ raise ArgumentError, <<~MSG
71
+ Invalid syntax for include tag:
84
72
 
85
- #{@params}
73
+ #{@params}
86
74
 
87
- Valid syntax:
75
+ Valid syntax:
88
76
 
89
- #{syntax_example}
77
+ #{syntax_example}
90
78
 
91
- MSG
79
+ MSG
92
80
  end
93
81
  end
94
82
 
@@ -99,13 +87,7 @@ MSG
99
87
 
100
88
  # Render the variable if required
101
89
  def render_variable(context)
102
- if @file =~ VARIABLE_SYNTAX
103
- partial = context.registers[:site]
104
- .liquid_renderer
105
- .file("(variable)")
106
- .parse(@file)
107
- partial.render!(context)
108
- end
90
+ Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file)
109
91
  end
110
92
 
111
93
  def tag_includes_dirs(context)
@@ -115,7 +97,7 @@ MSG
115
97
  def locate_include_file(context, file, safe)
116
98
  includes_dirs = tag_includes_dirs(context)
117
99
  includes_dirs.each do |dir|
118
- path = File.join(dir.to_s, file.to_s)
100
+ path = PathManager.join(dir, file)
119
101
  return path if valid_include_file?(path, dir.to_s, safe)
120
102
  end
121
103
  raise IOError, could_not_locate_message(file, includes_dirs, safe)
@@ -147,17 +129,12 @@ MSG
147
129
  end
148
130
 
149
131
  def add_include_to_dependency(site, path, context)
150
- page = context.registers[:page]
151
- return unless page
152
- return unless page.key?("path")
153
-
154
- absolute_path = \
155
- if page["collection"]
156
- site.in_source_dir(site.config["collections_dir"], page["path"])
157
- else
158
- site.in_source_dir(page["path"])
159
- end
160
- site.regenerator.add_dependency(absolute_path, path)
132
+ if context.registers[:page]&.key?("path")
133
+ site.regenerator.add_dependency(
134
+ site.in_source_dir(context.registers[:page]["path"]),
135
+ path
136
+ )
137
+ end
161
138
  end
162
139
 
163
140
  def load_cached_partial(path, context)
@@ -202,8 +179,8 @@ MSG
202
179
  private
203
180
 
204
181
  def could_not_locate_message(file, includes_dirs, safe)
205
- message = "Could not locate the included file '#{file}' in any of "\
206
- "#{includes_dirs}. Ensure it exists in one of those directories and"
182
+ message = "Could not locate the included file '#{file}' in any of #{includes_dirs}. " \
183
+ "Ensure it exists in one of those directories and"
207
184
  message + if safe
208
185
  " is not a symlink as those are not allowed in safe mode."
209
186
  else
@@ -212,30 +189,87 @@ MSG
212
189
  end
213
190
  end
214
191
 
192
+ # Do not inherit from this class.
193
+ # TODO: Merge into the `Jekyll::Tags::IncludeTag` in v5.0
194
+ class OptimizedIncludeTag < IncludeTag
195
+ def render(context)
196
+ @site ||= context.registers[:site]
197
+
198
+ file = render_variable(context) || @file
199
+ validate_file_name(file)
200
+
201
+ @site.inclusions[file] ||= locate_include_file(file)
202
+ inclusion = @site.inclusions[file]
203
+
204
+ add_include_to_dependency(inclusion, context) if @site.config["incremental"]
205
+
206
+ context.stack do
207
+ context["include"] = parse_params(context) if @params
208
+ inclusion.render(context)
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ def locate_include_file(file)
215
+ @site.includes_load_paths.each do |dir|
216
+ path = PathManager.join(dir, file)
217
+ return Inclusion.new(@site, dir, file) if valid_include_file?(path, dir)
218
+ end
219
+ raise IOError, could_not_locate_message(file, @site.includes_load_paths, @site.safe)
220
+ end
221
+
222
+ def valid_include_file?(path, dir)
223
+ File.file?(path) && !outside_scope?(path, dir)
224
+ end
225
+
226
+ def outside_scope?(path, dir)
227
+ @site.safe && !realpath_prefixed_with?(path, dir)
228
+ end
229
+
230
+ def realpath_prefixed_with?(path, dir)
231
+ File.realpath(path).start_with?(dir)
232
+ rescue StandardError
233
+ false
234
+ end
235
+
236
+ def add_include_to_dependency(inclusion, context)
237
+ page = context.registers[:page]
238
+ return unless page&.key?("path")
239
+
240
+ absolute_path = \
241
+ if page["collection"]
242
+ @site.in_source_dir(@site.config["collections_dir"], page["path"])
243
+ else
244
+ @site.in_source_dir(page["path"])
245
+ end
246
+
247
+ @site.regenerator.add_dependency(absolute_path, inclusion.path)
248
+ end
249
+ end
250
+
215
251
  class IncludeRelativeTag < IncludeTag
216
252
  def tag_includes_dirs(context)
217
253
  Array(page_path(context)).freeze
218
254
  end
219
255
 
220
256
  def page_path(context)
221
- if context.registers[:page].nil?
222
- context.registers[:site].source
223
- else
224
- site = context.registers[:site]
225
- page_payload = context.registers[:page]
226
- resource_path = \
227
- if page_payload["collection"].nil?
228
- page_payload["path"]
229
- else
230
- File.join(site.config["collections_dir"], page_payload["path"])
231
- end
232
- resource_path.sub!(%r!/#excerpt\z!, "")
233
- site.in_source_dir File.dirname(resource_path)
234
- end
257
+ page, site = context.registers.values_at(:page, :site)
258
+ return site.source unless page
259
+
260
+ site.in_source_dir File.dirname(resource_path(page, site))
261
+ end
262
+
263
+ private
264
+
265
+ def resource_path(page, site)
266
+ path = page["path"]
267
+ path = File.join(site.config["collections_dir"], path) if page["collection"]
268
+ path.delete_suffix("/#excerpt")
235
269
  end
236
270
  end
237
271
  end
238
272
  end
239
273
 
240
- Liquid::Template.register_tag("include", Jekyll::Tags::IncludeTag)
274
+ Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
241
275
  Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
@@ -3,9 +3,11 @@
3
3
  module Jekyll
4
4
  module Tags
5
5
  class Link < Liquid::Tag
6
+ include Jekyll::Filters::URLFilters
7
+
6
8
  class << self
7
9
  def tag_name
8
- self.name.split("::").last.downcase
10
+ name.split("::").last.downcase
9
11
  end
10
12
  end
11
13
 
@@ -16,19 +18,22 @@ module Jekyll
16
18
  end
17
19
 
18
20
  def render(context)
21
+ @context = context
19
22
  site = context.registers[:site]
23
+ relative_path = Liquid::Template.parse(@relative_path).render(context)
24
+ relative_path_with_leading_slash = PathManager.join("", relative_path)
20
25
 
21
26
  site.each_site_file do |item|
22
- return item.url if item.relative_path == @relative_path
27
+ return relative_url(item) if item.relative_path == relative_path
23
28
  # This takes care of the case for static files that have a leading /
24
- return item.url if item.relative_path == "/#{@relative_path}"
29
+ return relative_url(item) if item.relative_path == relative_path_with_leading_slash
25
30
  end
26
31
 
27
- raise ArgumentError, <<-MSG
28
- Could not find document '#{@relative_path}' in tag '#{self.class.tag_name}'.
32
+ raise ArgumentError, <<~MSG
33
+ Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'.
29
34
 
30
- Make sure the document exists and the path is correct.
31
- MSG
35
+ Make sure the document exists and the path is correct.
36
+ MSG
32
37
  end
33
38
  end
34
39
  end
@@ -3,7 +3,7 @@
3
3
  module Jekyll
4
4
  module Tags
5
5
  class PostComparer
6
- MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!
6
+ MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
7
7
 
8
8
  attr_reader :path, :date, :slug, :name
9
9
 
@@ -13,17 +13,18 @@ module Jekyll
13
13
  all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
14
14
  unless all
15
15
  raise Jekyll::Errors::InvalidPostNameError,
16
- "'#{name}' does not contain valid date and/or title."
16
+ "'#{name}' does not contain valid date and/or title."
17
17
  end
18
18
 
19
- escaped_slug = Regexp.escape(slug)
20
- @name_regex = %r!^_posts/#{path}#{date}-#{escaped_slug}\.[^.]+|
21
- ^#{path}_posts/?#{date}-#{escaped_slug}\.[^.]+!x
19
+ basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
20
+ @name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}!
22
21
  end
23
22
 
24
23
  def post_date
25
- @post_date ||= Utils.parse_date(date,
26
- "\"#{date}\" does not contain valid date and/or title.")
24
+ @post_date ||= Utils.parse_date(
25
+ date,
26
+ "'#{date}' does not contain valid date and/or title."
27
+ )
27
28
  end
28
29
 
29
30
  def ==(other)
@@ -38,6 +39,7 @@ module Jekyll
38
39
  end
39
40
 
40
41
  private
42
+
41
43
  # Construct the directory-aware post slug for a Jekyll::Post
42
44
  #
43
45
  # other - the Jekyll::Post
@@ -48,53 +50,54 @@ module Jekyll
48
50
  if path.nil? || path == ""
49
51
  other.data["slug"]
50
52
  else
51
- path + "/" + other.data["slug"]
53
+ "#{path}/#{other.data["slug"]}"
52
54
  end
53
55
  end
54
56
  end
55
57
 
56
58
  class PostUrl < Liquid::Tag
59
+ include Jekyll::Filters::URLFilters
60
+
57
61
  def initialize(tag_name, post, tokens)
58
62
  super
59
63
  @orig_post = post.strip
60
64
  begin
61
65
  @post = PostComparer.new(@orig_post)
62
66
  rescue StandardError => e
63
- raise Jekyll::Errors::PostURLError, <<-MSG
64
- Could not parse name of post "#{@orig_post}" in tag 'post_url'.
65
-
66
- Make sure the post exists and the name is correct.
67
-
68
- #{e.class}: #{e.message}
69
- MSG
67
+ raise Jekyll::Errors::PostURLError, <<~MSG
68
+ Could not parse name of post "#{@orig_post}" in tag 'post_url'.
69
+ Make sure the post exists and the name is correct.
70
+ #{e.class}: #{e.message}
71
+ MSG
70
72
  end
71
73
  end
72
74
 
73
75
  def render(context)
76
+ @context = context
74
77
  site = context.registers[:site]
75
78
 
76
- site.posts.docs.each do |p|
77
- return p.url if @post == p
79
+ site.posts.docs.each do |document|
80
+ return relative_url(document) if @post == document
78
81
  end
79
82
 
80
83
  # New matching method did not match, fall back to old method
81
84
  # with deprecation warning if this matches
82
85
 
83
- site.posts.docs.each do |p|
84
- next unless @post.deprecated_equality p
85
- Jekyll::Deprecator.deprecation_message "A call to "\
86
- "'{% post_url #{@post.name} %}' did not match " \
87
- "a post using the new matching method of checking name " \
88
- "(path-date-slug) equality. Please make sure that you " \
89
- "change this tag to match the post's name exactly."
90
- return p.url
91
- end
86
+ site.posts.docs.each do |document|
87
+ next unless @post.deprecated_equality document
92
88
 
93
- raise Jekyll::Errors::PostURLError, <<-MSG
94
- Could not find post "#{@orig_post}" in tag 'post_url'.
89
+ Jekyll::Deprecator.deprecation_message(
90
+ "A call to '{% post_url #{@post.name} %}' did not match a post using the new " \
91
+ "matching method of checking name (path-date-slug) equality. Please make sure " \
92
+ "that you change this tag to match the post's name exactly."
93
+ )
94
+ return relative_url(document)
95
+ end
95
96
 
96
- Make sure the post exists and the name is correct.
97
- MSG
97
+ raise Jekyll::Errors::PostURLError, <<~MSG
98
+ Could not find post "#{@orig_post}" in tag 'post_url'.
99
+ Make sure the post exists and the name is correct.
100
+ MSG
98
101
  end
99
102
  end
100
103
  end