jekyll 4.2.1 → 4.3.2

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +474 -350
  3. data/LICENSE +21 -21
  4. data/README.markdown +83 -86
  5. data/exe/jekyll +57 -57
  6. data/lib/blank_template/_config.yml +3 -3
  7. data/lib/blank_template/_layouts/default.html +12 -12
  8. data/lib/blank_template/_sass/{main.scss → base.scss} +9 -9
  9. data/lib/blank_template/assets/css/main.scss +4 -4
  10. data/lib/blank_template/index.md +8 -8
  11. data/lib/jekyll/cache.rb +186 -190
  12. data/lib/jekyll/cleaner.rb +111 -111
  13. data/lib/jekyll/collection.rb +310 -309
  14. data/lib/jekyll/command.rb +105 -105
  15. data/lib/jekyll/commands/build.rb +82 -93
  16. data/lib/jekyll/commands/clean.rb +44 -45
  17. data/lib/jekyll/commands/doctor.rb +177 -177
  18. data/lib/jekyll/commands/help.rb +34 -34
  19. data/lib/jekyll/commands/new.rb +168 -169
  20. data/lib/jekyll/commands/new_theme.rb +39 -40
  21. data/lib/jekyll/commands/serve/live_reload_reactor.rb +119 -122
  22. data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
  23. data/lib/jekyll/commands/serve/mime_types_charset.json +71 -0
  24. data/lib/jekyll/commands/serve/servlet.rb +206 -202
  25. data/lib/jekyll/commands/serve/websockets.rb +81 -81
  26. data/lib/jekyll/commands/serve.rb +367 -362
  27. data/lib/jekyll/configuration.rb +313 -313
  28. data/lib/jekyll/converter.rb +54 -54
  29. data/lib/jekyll/converters/identity.rb +41 -41
  30. data/lib/jekyll/converters/markdown/kramdown_parser.rb +197 -199
  31. data/lib/jekyll/converters/markdown.rb +113 -113
  32. data/lib/jekyll/converters/smartypants.rb +70 -70
  33. data/lib/jekyll/convertible.rb +257 -257
  34. data/lib/jekyll/deprecator.rb +50 -50
  35. data/lib/jekyll/document.rb +543 -544
  36. data/lib/jekyll/drops/collection_drop.rb +20 -20
  37. data/lib/jekyll/drops/document_drop.rb +74 -70
  38. data/lib/jekyll/drops/drop.rb +293 -293
  39. data/lib/jekyll/drops/excerpt_drop.rb +23 -19
  40. data/lib/jekyll/drops/jekyll_drop.rb +32 -32
  41. data/lib/jekyll/drops/site_drop.rb +66 -66
  42. data/lib/jekyll/drops/static_file_drop.rb +14 -14
  43. data/lib/jekyll/drops/theme_drop.rb +36 -0
  44. data/lib/jekyll/drops/unified_payload_drop.rb +30 -26
  45. data/lib/jekyll/drops/url_drop.rb +140 -140
  46. data/lib/jekyll/entry_filter.rb +117 -121
  47. data/lib/jekyll/errors.rb +20 -20
  48. data/lib/jekyll/excerpt.rb +200 -201
  49. data/lib/jekyll/external.rb +75 -79
  50. data/lib/jekyll/filters/date_filters.rb +110 -110
  51. data/lib/jekyll/filters/grouping_filters.rb +64 -64
  52. data/lib/jekyll/filters/url_filters.rb +98 -98
  53. data/lib/jekyll/filters.rb +532 -535
  54. data/lib/jekyll/frontmatter_defaults.rb +238 -240
  55. data/lib/jekyll/generator.rb +5 -5
  56. data/lib/jekyll/hooks.rb +107 -107
  57. data/lib/jekyll/inclusion.rb +32 -32
  58. data/lib/jekyll/layout.rb +55 -67
  59. data/lib/jekyll/liquid_extensions.rb +22 -22
  60. data/lib/jekyll/liquid_renderer/file.rb +77 -77
  61. data/lib/jekyll/liquid_renderer/table.rb +45 -55
  62. data/lib/jekyll/liquid_renderer.rb +80 -80
  63. data/lib/jekyll/log_adapter.rb +151 -151
  64. data/lib/jekyll/mime.types +939 -866
  65. data/lib/jekyll/page.rb +215 -217
  66. data/lib/jekyll/page_excerpt.rb +25 -25
  67. data/lib/jekyll/page_without_a_file.rb +14 -14
  68. data/lib/jekyll/path_manager.rb +74 -74
  69. data/lib/jekyll/plugin.rb +92 -92
  70. data/lib/jekyll/plugin_manager.rb +123 -115
  71. data/lib/jekyll/profiler.rb +51 -58
  72. data/lib/jekyll/publisher.rb +23 -23
  73. data/lib/jekyll/reader.rb +209 -192
  74. data/lib/jekyll/readers/collection_reader.rb +23 -23
  75. data/lib/jekyll/readers/data_reader.rb +113 -79
  76. data/lib/jekyll/readers/layout_reader.rb +62 -62
  77. data/lib/jekyll/readers/page_reader.rb +25 -25
  78. data/lib/jekyll/readers/post_reader.rb +85 -85
  79. data/lib/jekyll/readers/static_file_reader.rb +25 -25
  80. data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
  81. data/lib/jekyll/regenerator.rb +195 -195
  82. data/lib/jekyll/related_posts.rb +52 -52
  83. data/lib/jekyll/renderer.rb +263 -265
  84. data/lib/jekyll/site.rb +576 -551
  85. data/lib/jekyll/static_file.rb +205 -208
  86. data/lib/jekyll/stevenson.rb +60 -60
  87. data/lib/jekyll/tags/highlight.rb +114 -110
  88. data/lib/jekyll/tags/include.rb +275 -275
  89. data/lib/jekyll/tags/link.rb +42 -42
  90. data/lib/jekyll/tags/post_url.rb +106 -106
  91. data/lib/jekyll/theme.rb +90 -86
  92. data/lib/jekyll/theme_builder.rb +121 -121
  93. data/lib/jekyll/url.rb +167 -167
  94. data/lib/jekyll/utils/ansi.rb +57 -57
  95. data/lib/jekyll/utils/exec.rb +26 -26
  96. data/lib/jekyll/utils/internet.rb +37 -37
  97. data/lib/jekyll/utils/platforms.rb +67 -67
  98. data/lib/jekyll/utils/thread_event.rb +31 -31
  99. data/lib/jekyll/utils/win_tz.rb +46 -75
  100. data/lib/jekyll/utils.rb +371 -367
  101. data/lib/jekyll/version.rb +5 -5
  102. data/lib/jekyll.rb +195 -195
  103. data/lib/site_template/.gitignore +5 -5
  104. data/lib/site_template/404.html +25 -25
  105. data/lib/site_template/_config.yml +55 -55
  106. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
  107. data/lib/site_template/about.markdown +18 -18
  108. data/lib/site_template/index.markdown +6 -6
  109. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
  110. data/lib/theme_template/Gemfile +4 -4
  111. data/lib/theme_template/LICENSE.txt.erb +21 -21
  112. data/lib/theme_template/README.md.erb +50 -52
  113. data/lib/theme_template/_layouts/default.html +1 -1
  114. data/lib/theme_template/_layouts/page.html +5 -5
  115. data/lib/theme_template/_layouts/post.html +5 -5
  116. data/lib/theme_template/example/_config.yml.erb +1 -1
  117. data/lib/theme_template/example/_post.md +12 -12
  118. data/lib/theme_template/example/index.html +14 -14
  119. data/lib/theme_template/example/style.scss +7 -7
  120. data/lib/theme_template/gitignore.erb +6 -6
  121. data/lib/theme_template/theme.gemspec.erb +16 -16
  122. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
  123. data/rubocop/jekyll/no_p_allowed.rb +23 -23
  124. data/rubocop/jekyll/no_puts_allowed.rb +23 -23
  125. data/rubocop/jekyll.rb +5 -5
  126. metadata +64 -18
@@ -1,265 +1,263 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class Renderer
5
- attr_reader :document, :site
6
- attr_writer :layouts, :payload
7
-
8
- def initialize(site, document, site_payload = nil)
9
- @site = site
10
- @document = document
11
- @payload = site_payload
12
- @layouts = nil
13
- end
14
-
15
- # Fetches the payload used in Liquid rendering.
16
- # It can be written with #payload=(new_payload)
17
- # Falls back to site.site_payload if no payload is set.
18
- #
19
- # Returns a Jekyll::Drops::UnifiedPayloadDrop
20
- def payload
21
- @payload ||= site.site_payload
22
- end
23
-
24
- # The list of layouts registered for this Renderer.
25
- # It can be written with #layouts=(new_layouts)
26
- # Falls back to site.layouts if no layouts are registered.
27
- #
28
- # Returns a Hash of String => Jekyll::Layout identified
29
- # as basename without the extension name.
30
- def layouts
31
- @layouts || site.layouts
32
- end
33
-
34
- # Determine which converters to use based on this document's
35
- # extension.
36
- #
37
- # Returns Array of Converter instances.
38
- def converters
39
- @converters ||= site.converters.select { |c| c.matches(document.extname) }.tap(&:sort!)
40
- end
41
-
42
- # Determine the extname the outputted file should have
43
- #
44
- # Returns String the output extname including the leading period.
45
- def output_ext
46
- @output_ext ||= (permalink_ext || converter_output_ext)
47
- end
48
-
49
- # Prepare payload and render the document
50
- #
51
- # Returns String rendered document output
52
- def run
53
- Jekyll.logger.debug "Rendering:", document.relative_path
54
-
55
- assign_pages!
56
- assign_current_document!
57
- assign_highlighter_options!
58
- assign_layout_data!
59
-
60
- Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path
61
- document.trigger_hooks(:pre_render, payload)
62
-
63
- render_document
64
- end
65
-
66
- # Render the document.
67
- #
68
- # Returns String rendered document output
69
- # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
70
- def render_document
71
- info = {
72
- :registers => { :site => site, :page => payload["page"] },
73
- :strict_filters => liquid_options["strict_filters"],
74
- :strict_variables => liquid_options["strict_variables"],
75
- }
76
-
77
- output = document.content
78
- if document.render_with_liquid?
79
- Jekyll.logger.debug "Rendering Liquid:", document.relative_path
80
- output = render_liquid(output, payload, info, document.path)
81
- end
82
-
83
- Jekyll.logger.debug "Rendering Markup:", document.relative_path
84
- output = convert(output.to_s)
85
- document.content = output
86
-
87
- Jekyll.logger.debug "Post-Convert Hooks:", document.relative_path
88
- document.trigger_hooks(:post_convert)
89
- output = document.content
90
-
91
- if document.place_in_layout?
92
- Jekyll.logger.debug "Rendering Layout:", document.relative_path
93
- output = place_in_layouts(output, payload, info)
94
- end
95
-
96
- output
97
- end
98
- # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
99
-
100
- # Convert the document using the converters which match this renderer's document.
101
- #
102
- # Returns String the converted content.
103
- def convert(content)
104
- converters.reduce(content) do |output, converter|
105
- begin
106
- converter.convert output
107
- rescue StandardError => e
108
- Jekyll.logger.error "Conversion error:",
109
- "#{converter.class} encountered an error while "\
110
- "converting '#{document.relative_path}':"
111
- Jekyll.logger.error("", e.to_s)
112
- raise e
113
- end
114
- end
115
- end
116
-
117
- # Render the given content with the payload and info
118
- #
119
- # content -
120
- # payload -
121
- # info -
122
- # path - (optional) the path to the file, for use in ex
123
- #
124
- # Returns String the content, rendered by Liquid.
125
- def render_liquid(content, payload, info, path = nil)
126
- template = site.liquid_renderer.file(path).parse(content)
127
- template.warnings.each do |e|
128
- Jekyll.logger.warn "Liquid Warning:",
129
- LiquidRenderer.format_error(e, path || document.relative_path)
130
- end
131
- template.render!(payload, info)
132
- # rubocop: disable Lint/RescueException
133
- rescue Exception => e
134
- Jekyll.logger.error "Liquid Exception:",
135
- LiquidRenderer.format_error(e, path || document.relative_path)
136
- raise e
137
- end
138
- # rubocop: enable Lint/RescueException
139
-
140
- # Checks if the layout specified in the document actually exists
141
- #
142
- # layout - the layout to check
143
- #
144
- # Returns Boolean true if the layout is invalid, false if otherwise
145
- def invalid_layout?(layout)
146
- !document.data["layout"].nil? && layout.nil? && !(document.is_a? Jekyll::Excerpt)
147
- end
148
-
149
- # Render layouts and place document content inside.
150
- #
151
- # Returns String rendered content
152
- def place_in_layouts(content, payload, info)
153
- output = content.dup
154
- layout = layouts[document.data["layout"].to_s]
155
- validate_layout(layout)
156
-
157
- used = Set.new([layout])
158
-
159
- # Reset the payload layout data to ensure it starts fresh for each page.
160
- payload["layout"] = nil
161
-
162
- while layout
163
- output = render_layout(output, layout, info)
164
- add_regenerator_dependencies(layout)
165
-
166
- next unless (layout = site.layouts[layout.data["layout"]])
167
- break if used.include?(layout)
168
-
169
- used << layout
170
- end
171
- output
172
- end
173
-
174
- private
175
-
176
- # Checks if the layout specified in the document actually exists
177
- #
178
- # layout - the layout to check
179
- # Returns nothing
180
- def validate_layout(layout)
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."
185
- end
186
-
187
- # Render layout content into document.output
188
- #
189
- # Returns String rendered content
190
- def render_layout(output, layout, info)
191
- payload["content"] = output
192
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
193
-
194
- render_liquid(
195
- layout.content,
196
- payload,
197
- info,
198
- layout.path
199
- )
200
- end
201
-
202
- def add_regenerator_dependencies(layout)
203
- return unless document.write?
204
-
205
- site.regenerator.add_dependency(
206
- site.in_source_dir(document.path),
207
- layout.path
208
- )
209
- end
210
-
211
- # Set page content to payload and assign pager if document has one.
212
- #
213
- # Returns nothing
214
- def assign_pages!
215
- payload["page"] = document.to_liquid
216
- payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager))
217
- end
218
-
219
- # Set related posts to payload if document is a post.
220
- #
221
- # Returns nothing
222
- def assign_current_document!
223
- payload["site"].current_document = document
224
- end
225
-
226
- # Set highlighter prefix and suffix
227
- #
228
- # Returns nothing
229
- def assign_highlighter_options!
230
- payload["highlighter_prefix"] = converters.first.highlighter_prefix
231
- payload["highlighter_suffix"] = converters.first.highlighter_suffix
232
- end
233
-
234
- def assign_layout_data!
235
- layout = layouts[document.data["layout"]]
236
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout
237
- end
238
-
239
- def permalink_ext
240
- document_permalink = document.permalink
241
- if document_permalink && !document_permalink.end_with?("/")
242
- permalink_ext = File.extname(document_permalink)
243
- permalink_ext unless permalink_ext.empty?
244
- end
245
- end
246
-
247
- def converter_output_ext
248
- if output_exts.size == 1
249
- output_exts.last
250
- else
251
- output_exts[-2]
252
- end
253
- end
254
-
255
- def output_exts
256
- @output_exts ||= converters.map do |c|
257
- c.output_ext(document.extname)
258
- end.tap(&:compact!)
259
- end
260
-
261
- def liquid_options
262
- @liquid_options ||= site.config["liquid"]
263
- end
264
- end
265
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ class Renderer
5
+ attr_reader :document, :site
6
+ attr_writer :layouts, :payload
7
+
8
+ def initialize(site, document, site_payload = nil)
9
+ @site = site
10
+ @document = document
11
+ @payload = site_payload
12
+ @layouts = nil
13
+ end
14
+
15
+ # Fetches the payload used in Liquid rendering.
16
+ # It can be written with #payload=(new_payload)
17
+ # Falls back to site.site_payload if no payload is set.
18
+ #
19
+ # Returns a Jekyll::Drops::UnifiedPayloadDrop
20
+ def payload
21
+ @payload ||= site.site_payload
22
+ end
23
+
24
+ # The list of layouts registered for this Renderer.
25
+ # It can be written with #layouts=(new_layouts)
26
+ # Falls back to site.layouts if no layouts are registered.
27
+ #
28
+ # Returns a Hash of String => Jekyll::Layout identified
29
+ # as basename without the extension name.
30
+ def layouts
31
+ @layouts || site.layouts
32
+ end
33
+
34
+ # Determine which converters to use based on this document's
35
+ # extension.
36
+ #
37
+ # Returns Array of Converter instances.
38
+ def converters
39
+ @converters ||= site.converters.select { |c| c.matches(document.extname) }.tap(&:sort!)
40
+ end
41
+
42
+ # Determine the extname the outputted file should have
43
+ #
44
+ # Returns String the output extname including the leading period.
45
+ def output_ext
46
+ @output_ext ||= (permalink_ext || converter_output_ext)
47
+ end
48
+
49
+ # Prepare payload and render the document
50
+ #
51
+ # Returns String rendered document output
52
+ def run
53
+ Jekyll.logger.debug "Rendering:", document.relative_path
54
+
55
+ assign_pages!
56
+ assign_current_document!
57
+ assign_highlighter_options!
58
+ assign_layout_data!
59
+
60
+ Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path
61
+ document.trigger_hooks(:pre_render, payload)
62
+
63
+ render_document
64
+ end
65
+
66
+ # Render the document.
67
+ #
68
+ # Returns String rendered document output
69
+ # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
70
+ def render_document
71
+ info = {
72
+ :registers => { :site => site, :page => payload["page"] },
73
+ :strict_filters => liquid_options["strict_filters"],
74
+ :strict_variables => liquid_options["strict_variables"],
75
+ }
76
+
77
+ output = document.content
78
+ if document.render_with_liquid?
79
+ Jekyll.logger.debug "Rendering Liquid:", document.relative_path
80
+ output = render_liquid(output, payload, info, document.path)
81
+ end
82
+
83
+ Jekyll.logger.debug "Rendering Markup:", document.relative_path
84
+ output = convert(output.to_s)
85
+ document.content = output
86
+
87
+ Jekyll.logger.debug "Post-Convert Hooks:", document.relative_path
88
+ document.trigger_hooks(:post_convert)
89
+ output = document.content
90
+
91
+ if document.place_in_layout?
92
+ Jekyll.logger.debug "Rendering Layout:", document.relative_path
93
+ output = place_in_layouts(output, payload, info)
94
+ end
95
+
96
+ output
97
+ end
98
+ # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
99
+
100
+ # Convert the document using the converters which match this renderer's document.
101
+ #
102
+ # Returns String the converted content.
103
+ def convert(content)
104
+ converters.reduce(content) do |output, converter|
105
+ converter.convert output
106
+ rescue StandardError => e
107
+ Jekyll.logger.error "Conversion error:",
108
+ "#{converter.class} encountered an error while " \
109
+ "converting '#{document.relative_path}':"
110
+ Jekyll.logger.error("", e.to_s)
111
+ raise e
112
+ end
113
+ end
114
+
115
+ # Render the given content with the payload and info
116
+ #
117
+ # content -
118
+ # payload -
119
+ # info -
120
+ # path - (optional) the path to the file, for use in ex
121
+ #
122
+ # Returns String the content, rendered by Liquid.
123
+ def render_liquid(content, payload, info, path = nil)
124
+ template = site.liquid_renderer.file(path).parse(content)
125
+ template.warnings.each do |e|
126
+ Jekyll.logger.warn "Liquid Warning:",
127
+ LiquidRenderer.format_error(e, path || document.relative_path)
128
+ end
129
+ template.render!(payload, info)
130
+ # rubocop: disable Lint/RescueException
131
+ rescue Exception => e
132
+ Jekyll.logger.error "Liquid Exception:",
133
+ LiquidRenderer.format_error(e, path || document.relative_path)
134
+ raise e
135
+ end
136
+ # rubocop: enable Lint/RescueException
137
+
138
+ # Checks if the layout specified in the document actually exists
139
+ #
140
+ # layout - the layout to check
141
+ #
142
+ # Returns Boolean true if the layout is invalid, false if otherwise
143
+ def invalid_layout?(layout)
144
+ !document.data["layout"].nil? && layout.nil? && !(document.is_a? Jekyll::Excerpt)
145
+ end
146
+
147
+ # Render layouts and place document content inside.
148
+ #
149
+ # Returns String rendered content
150
+ def place_in_layouts(content, payload, info)
151
+ output = content.dup
152
+ layout = layouts[document.data["layout"].to_s]
153
+ validate_layout(layout)
154
+
155
+ used = Set.new([layout])
156
+
157
+ # Reset the payload layout data to ensure it starts fresh for each page.
158
+ payload["layout"] = nil
159
+
160
+ while layout
161
+ output = render_layout(output, layout, info)
162
+ add_regenerator_dependencies(layout)
163
+
164
+ next unless (layout = site.layouts[layout.data["layout"]])
165
+ break if used.include?(layout)
166
+
167
+ used << layout
168
+ end
169
+ output
170
+ end
171
+
172
+ private
173
+
174
+ # Checks if the layout specified in the document actually exists
175
+ #
176
+ # layout - the layout to check
177
+ # Returns nothing
178
+ def validate_layout(layout)
179
+ return unless invalid_layout?(layout)
180
+
181
+ Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
182
+ "in #{document.relative_path} does not exist."
183
+ end
184
+
185
+ # Render layout content into document.output
186
+ #
187
+ # Returns String rendered content
188
+ def render_layout(output, layout, info)
189
+ payload["content"] = output
190
+ payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
191
+
192
+ render_liquid(
193
+ layout.content,
194
+ payload,
195
+ info,
196
+ layout.path
197
+ )
198
+ end
199
+
200
+ def add_regenerator_dependencies(layout)
201
+ return unless document.write?
202
+
203
+ site.regenerator.add_dependency(
204
+ site.in_source_dir(document.path),
205
+ layout.path
206
+ )
207
+ end
208
+
209
+ # Set page content to payload and assign pager if document has one.
210
+ #
211
+ # Returns nothing
212
+ def assign_pages!
213
+ payload["page"] = document.to_liquid
214
+ payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager))
215
+ end
216
+
217
+ # Set related posts to payload if document is a post.
218
+ #
219
+ # Returns nothing
220
+ def assign_current_document!
221
+ payload["site"].current_document = document
222
+ end
223
+
224
+ # Set highlighter prefix and suffix
225
+ #
226
+ # Returns nothing
227
+ def assign_highlighter_options!
228
+ payload["highlighter_prefix"] = converters.first.highlighter_prefix
229
+ payload["highlighter_suffix"] = converters.first.highlighter_suffix
230
+ end
231
+
232
+ def assign_layout_data!
233
+ layout = layouts[document.data["layout"]]
234
+ payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout
235
+ end
236
+
237
+ def permalink_ext
238
+ document_permalink = document.permalink
239
+ if document_permalink && !document_permalink.end_with?("/")
240
+ permalink_ext = File.extname(document_permalink)
241
+ permalink_ext unless permalink_ext.empty?
242
+ end
243
+ end
244
+
245
+ def converter_output_ext
246
+ if output_exts.size == 1
247
+ output_exts.last
248
+ else
249
+ output_exts[-2]
250
+ end
251
+ end
252
+
253
+ def output_exts
254
+ @output_exts ||= converters.map do |c|
255
+ c.output_ext(document.extname)
256
+ end.tap(&:compact!)
257
+ end
258
+
259
+ def liquid_options
260
+ @liquid_options ||= site.config["liquid"]
261
+ end
262
+ end
263
+ end