jekyll 4.2.1 → 4.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +350 -350
- data/LICENSE +21 -21
- data/README.markdown +86 -86
- data/exe/jekyll +57 -57
- data/lib/blank_template/_config.yml +3 -3
- data/lib/blank_template/_layouts/default.html +12 -12
- data/lib/blank_template/_sass/main.scss +9 -9
- data/lib/blank_template/assets/css/main.scss +4 -4
- data/lib/blank_template/index.md +8 -8
- data/lib/jekyll/cache.rb +190 -190
- data/lib/jekyll/cleaner.rb +111 -111
- data/lib/jekyll/collection.rb +309 -309
- data/lib/jekyll/command.rb +105 -105
- data/lib/jekyll/commands/build.rb +93 -93
- data/lib/jekyll/commands/clean.rb +45 -45
- data/lib/jekyll/commands/doctor.rb +177 -177
- data/lib/jekyll/commands/help.rb +34 -34
- data/lib/jekyll/commands/new.rb +172 -169
- data/lib/jekyll/commands/new_theme.rb +40 -40
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +122 -122
- data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
- data/lib/jekyll/commands/serve/servlet.rb +202 -202
- data/lib/jekyll/commands/serve/websockets.rb +81 -81
- data/lib/jekyll/commands/serve.rb +362 -362
- data/lib/jekyll/configuration.rb +313 -313
- data/lib/jekyll/converter.rb +54 -54
- data/lib/jekyll/converters/identity.rb +41 -41
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +199 -199
- data/lib/jekyll/converters/markdown.rb +113 -113
- data/lib/jekyll/converters/smartypants.rb +70 -70
- data/lib/jekyll/convertible.rb +257 -257
- data/lib/jekyll/deprecator.rb +50 -50
- data/lib/jekyll/document.rb +544 -544
- data/lib/jekyll/drops/collection_drop.rb +20 -20
- data/lib/jekyll/drops/document_drop.rb +70 -70
- data/lib/jekyll/drops/drop.rb +293 -293
- data/lib/jekyll/drops/excerpt_drop.rb +19 -19
- data/lib/jekyll/drops/jekyll_drop.rb +32 -32
- data/lib/jekyll/drops/site_drop.rb +66 -66
- data/lib/jekyll/drops/static_file_drop.rb +14 -14
- data/lib/jekyll/drops/unified_payload_drop.rb +26 -26
- data/lib/jekyll/drops/url_drop.rb +140 -140
- data/lib/jekyll/entry_filter.rb +121 -121
- data/lib/jekyll/errors.rb +20 -20
- data/lib/jekyll/excerpt.rb +201 -201
- data/lib/jekyll/external.rb +79 -79
- data/lib/jekyll/filters/date_filters.rb +110 -110
- data/lib/jekyll/filters/grouping_filters.rb +64 -64
- data/lib/jekyll/filters/url_filters.rb +98 -98
- data/lib/jekyll/filters.rb +535 -535
- data/lib/jekyll/frontmatter_defaults.rb +240 -240
- data/lib/jekyll/generator.rb +5 -5
- data/lib/jekyll/hooks.rb +107 -107
- data/lib/jekyll/inclusion.rb +32 -32
- data/lib/jekyll/layout.rb +67 -67
- data/lib/jekyll/liquid_extensions.rb +22 -22
- data/lib/jekyll/liquid_renderer/file.rb +77 -77
- data/lib/jekyll/liquid_renderer/table.rb +55 -55
- data/lib/jekyll/liquid_renderer.rb +80 -80
- data/lib/jekyll/log_adapter.rb +151 -151
- data/lib/jekyll/mime.types +866 -866
- data/lib/jekyll/page.rb +217 -217
- data/lib/jekyll/page_excerpt.rb +25 -25
- data/lib/jekyll/page_without_a_file.rb +14 -14
- data/lib/jekyll/path_manager.rb +74 -74
- data/lib/jekyll/plugin.rb +92 -92
- data/lib/jekyll/plugin_manager.rb +115 -115
- data/lib/jekyll/profiler.rb +58 -58
- data/lib/jekyll/publisher.rb +23 -23
- data/lib/jekyll/reader.rb +192 -192
- data/lib/jekyll/readers/collection_reader.rb +23 -23
- data/lib/jekyll/readers/data_reader.rb +79 -79
- data/lib/jekyll/readers/layout_reader.rb +62 -62
- data/lib/jekyll/readers/page_reader.rb +25 -25
- data/lib/jekyll/readers/post_reader.rb +85 -85
- data/lib/jekyll/readers/static_file_reader.rb +25 -25
- data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
- data/lib/jekyll/regenerator.rb +195 -195
- data/lib/jekyll/related_posts.rb +52 -52
- data/lib/jekyll/renderer.rb +265 -265
- data/lib/jekyll/site.rb +551 -551
- data/lib/jekyll/static_file.rb +208 -208
- data/lib/jekyll/stevenson.rb +60 -60
- data/lib/jekyll/tags/highlight.rb +110 -110
- data/lib/jekyll/tags/include.rb +275 -275
- data/lib/jekyll/tags/link.rb +42 -42
- data/lib/jekyll/tags/post_url.rb +106 -106
- data/lib/jekyll/theme.rb +86 -86
- data/lib/jekyll/theme_builder.rb +121 -121
- data/lib/jekyll/url.rb +167 -167
- data/lib/jekyll/utils/ansi.rb +57 -57
- data/lib/jekyll/utils/exec.rb +26 -26
- data/lib/jekyll/utils/internet.rb +37 -37
- data/lib/jekyll/utils/platforms.rb +67 -67
- data/lib/jekyll/utils/thread_event.rb +31 -31
- data/lib/jekyll/utils/win_tz.rb +75 -75
- data/lib/jekyll/utils.rb +367 -367
- data/lib/jekyll/version.rb +5 -5
- data/lib/jekyll.rb +195 -195
- data/lib/site_template/.gitignore +5 -5
- data/lib/site_template/404.html +25 -25
- data/lib/site_template/_config.yml +55 -55
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
- data/lib/site_template/about.markdown +18 -18
- data/lib/site_template/index.markdown +6 -6
- data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
- data/lib/theme_template/Gemfile +4 -4
- data/lib/theme_template/LICENSE.txt.erb +21 -21
- data/lib/theme_template/README.md.erb +52 -52
- data/lib/theme_template/_layouts/default.html +1 -1
- data/lib/theme_template/_layouts/page.html +5 -5
- data/lib/theme_template/_layouts/post.html +5 -5
- data/lib/theme_template/example/_config.yml.erb +1 -1
- data/lib/theme_template/example/_post.md +12 -12
- data/lib/theme_template/example/index.html +14 -14
- data/lib/theme_template/example/style.scss +7 -7
- data/lib/theme_template/gitignore.erb +6 -6
- data/lib/theme_template/theme.gemspec.erb +16 -16
- data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
- data/rubocop/jekyll/no_p_allowed.rb +23 -23
- data/rubocop/jekyll/no_puts_allowed.rb +23 -23
- data/rubocop/jekyll.rb +5 -5
- metadata +3 -3
data/lib/jekyll/tags/include.rb
CHANGED
|
@@ -1,275 +1,275 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jekyll
|
|
4
|
-
module Tags
|
|
5
|
-
class IncludeTag < Liquid::Tag
|
|
6
|
-
VALID_SYNTAX = %r!
|
|
7
|
-
([\w-]+)\s*=\s*
|
|
8
|
-
(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+))
|
|
9
|
-
!x.freeze
|
|
10
|
-
VARIABLE_SYNTAX = %r!
|
|
11
|
-
(?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+)
|
|
12
|
-
(?<params>.*)
|
|
13
|
-
!mx.freeze
|
|
14
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
def initialize(tag_name, markup, tokens)
|
|
20
|
-
super
|
|
21
|
-
markup = markup.strip
|
|
22
|
-
matched = markup.match(VARIABLE_SYNTAX)
|
|
23
|
-
if matched
|
|
24
|
-
@file = matched["variable"].strip
|
|
25
|
-
@params = matched["params"].strip
|
|
26
|
-
else
|
|
27
|
-
@file, @params = markup.split(%r!\s+!, 2)
|
|
28
|
-
end
|
|
29
|
-
validate_params if @params
|
|
30
|
-
@tag_name = tag_name
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def syntax_example
|
|
34
|
-
"{% #{@tag_name} file.ext param='value' param2='value' %}"
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def parse_params(context)
|
|
38
|
-
params = {}
|
|
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]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
params[key] = value
|
|
49
|
-
end
|
|
50
|
-
params
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def validate_file_name(file)
|
|
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:
|
|
57
|
-
|
|
58
|
-
#{file}
|
|
59
|
-
|
|
60
|
-
Valid syntax:
|
|
61
|
-
|
|
62
|
-
#{syntax_example}
|
|
63
|
-
|
|
64
|
-
MSG
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def validate_params
|
|
69
|
-
unless FULL_VALID_SYNTAX.match?(@params)
|
|
70
|
-
raise ArgumentError, <<~MSG
|
|
71
|
-
Invalid syntax for include tag:
|
|
72
|
-
|
|
73
|
-
#{@params}
|
|
74
|
-
|
|
75
|
-
Valid syntax:
|
|
76
|
-
|
|
77
|
-
#{syntax_example}
|
|
78
|
-
|
|
79
|
-
MSG
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Grab file read opts in the context
|
|
84
|
-
def file_read_opts(context)
|
|
85
|
-
context.registers[:site].file_read_opts
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Render the variable if required
|
|
89
|
-
def render_variable(context)
|
|
90
|
-
Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def tag_includes_dirs(context)
|
|
94
|
-
context.registers[:site].includes_load_paths.freeze
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def locate_include_file(context, file, safe)
|
|
98
|
-
includes_dirs = tag_includes_dirs(context)
|
|
99
|
-
includes_dirs.each do |dir|
|
|
100
|
-
path = PathManager.join(dir, file)
|
|
101
|
-
return path if valid_include_file?(path, dir.to_s, safe)
|
|
102
|
-
end
|
|
103
|
-
raise IOError, could_not_locate_message(file, includes_dirs, safe)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def render(context)
|
|
107
|
-
site = context.registers[:site]
|
|
108
|
-
|
|
109
|
-
file = render_variable(context) || @file
|
|
110
|
-
validate_file_name(file)
|
|
111
|
-
|
|
112
|
-
path = locate_include_file(context, file, site.safe)
|
|
113
|
-
return unless path
|
|
114
|
-
|
|
115
|
-
add_include_to_dependency(site, path, context)
|
|
116
|
-
|
|
117
|
-
partial = load_cached_partial(path, context)
|
|
118
|
-
|
|
119
|
-
context.stack do
|
|
120
|
-
context["include"] = parse_params(context) if @params
|
|
121
|
-
begin
|
|
122
|
-
partial.render!(context)
|
|
123
|
-
rescue Liquid::Error => e
|
|
124
|
-
e.template_name = path
|
|
125
|
-
e.markup_context = "included " if e.markup_context.nil?
|
|
126
|
-
raise e
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def add_include_to_dependency(site, path, context)
|
|
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
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def load_cached_partial(path, context)
|
|
141
|
-
context.registers[:cached_partials] ||= {}
|
|
142
|
-
cached_partial = context.registers[:cached_partials]
|
|
143
|
-
|
|
144
|
-
if cached_partial.key?(path)
|
|
145
|
-
cached_partial[path]
|
|
146
|
-
else
|
|
147
|
-
unparsed_file = context.registers[:site]
|
|
148
|
-
.liquid_renderer
|
|
149
|
-
.file(path)
|
|
150
|
-
begin
|
|
151
|
-
cached_partial[path] = unparsed_file.parse(read_file(path, context))
|
|
152
|
-
rescue Liquid::Error => e
|
|
153
|
-
e.template_name = path
|
|
154
|
-
e.markup_context = "included " if e.markup_context.nil?
|
|
155
|
-
raise e
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def valid_include_file?(path, dir, safe)
|
|
161
|
-
!outside_site_source?(path, dir, safe) && File.file?(path)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def outside_site_source?(path, dir, safe)
|
|
165
|
-
safe && !realpath_prefixed_with?(path, dir)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def realpath_prefixed_with?(path, dir)
|
|
169
|
-
File.exist?(path) && File.realpath(path).start_with?(dir)
|
|
170
|
-
rescue StandardError
|
|
171
|
-
false
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# This method allows to modify the file content by inheriting from the class.
|
|
175
|
-
def read_file(file, context)
|
|
176
|
-
File.read(file, **file_read_opts(context))
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
def could_not_locate_message(file, includes_dirs, safe)
|
|
182
|
-
message = "Could not locate the included file '#{file}' in any of "\
|
|
183
|
-
"#{includes_dirs}. Ensure it exists in one of those directories and"
|
|
184
|
-
message + if safe
|
|
185
|
-
" is not a symlink as those are not allowed in safe mode."
|
|
186
|
-
else
|
|
187
|
-
", if it is a symlink, does not point outside your site source."
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
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
|
-
|
|
251
|
-
class IncludeRelativeTag < IncludeTag
|
|
252
|
-
def tag_includes_dirs(context)
|
|
253
|
-
Array(page_path(context)).freeze
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
def page_path(context)
|
|
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.sub(%r!/#excerpt\z!, "")
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
|
|
275
|
-
Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Tags
|
|
5
|
+
class IncludeTag < Liquid::Tag
|
|
6
|
+
VALID_SYNTAX = %r!
|
|
7
|
+
([\w-]+)\s*=\s*
|
|
8
|
+
(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+))
|
|
9
|
+
!x.freeze
|
|
10
|
+
VARIABLE_SYNTAX = %r!
|
|
11
|
+
(?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+)
|
|
12
|
+
(?<params>.*)
|
|
13
|
+
!mx.freeze
|
|
14
|
+
|
|
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
|
|
18
|
+
|
|
19
|
+
def initialize(tag_name, markup, tokens)
|
|
20
|
+
super
|
|
21
|
+
markup = markup.strip
|
|
22
|
+
matched = markup.match(VARIABLE_SYNTAX)
|
|
23
|
+
if matched
|
|
24
|
+
@file = matched["variable"].strip
|
|
25
|
+
@params = matched["params"].strip
|
|
26
|
+
else
|
|
27
|
+
@file, @params = markup.split(%r!\s+!, 2)
|
|
28
|
+
end
|
|
29
|
+
validate_params if @params
|
|
30
|
+
@tag_name = tag_name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def syntax_example
|
|
34
|
+
"{% #{@tag_name} file.ext param='value' param2='value' %}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse_params(context)
|
|
38
|
+
params = {}
|
|
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]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
params[key] = value
|
|
49
|
+
end
|
|
50
|
+
params
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def validate_file_name(file)
|
|
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:
|
|
57
|
+
|
|
58
|
+
#{file}
|
|
59
|
+
|
|
60
|
+
Valid syntax:
|
|
61
|
+
|
|
62
|
+
#{syntax_example}
|
|
63
|
+
|
|
64
|
+
MSG
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_params
|
|
69
|
+
unless FULL_VALID_SYNTAX.match?(@params)
|
|
70
|
+
raise ArgumentError, <<~MSG
|
|
71
|
+
Invalid syntax for include tag:
|
|
72
|
+
|
|
73
|
+
#{@params}
|
|
74
|
+
|
|
75
|
+
Valid syntax:
|
|
76
|
+
|
|
77
|
+
#{syntax_example}
|
|
78
|
+
|
|
79
|
+
MSG
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Grab file read opts in the context
|
|
84
|
+
def file_read_opts(context)
|
|
85
|
+
context.registers[:site].file_read_opts
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Render the variable if required
|
|
89
|
+
def render_variable(context)
|
|
90
|
+
Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def tag_includes_dirs(context)
|
|
94
|
+
context.registers[:site].includes_load_paths.freeze
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def locate_include_file(context, file, safe)
|
|
98
|
+
includes_dirs = tag_includes_dirs(context)
|
|
99
|
+
includes_dirs.each do |dir|
|
|
100
|
+
path = PathManager.join(dir, file)
|
|
101
|
+
return path if valid_include_file?(path, dir.to_s, safe)
|
|
102
|
+
end
|
|
103
|
+
raise IOError, could_not_locate_message(file, includes_dirs, safe)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def render(context)
|
|
107
|
+
site = context.registers[:site]
|
|
108
|
+
|
|
109
|
+
file = render_variable(context) || @file
|
|
110
|
+
validate_file_name(file)
|
|
111
|
+
|
|
112
|
+
path = locate_include_file(context, file, site.safe)
|
|
113
|
+
return unless path
|
|
114
|
+
|
|
115
|
+
add_include_to_dependency(site, path, context)
|
|
116
|
+
|
|
117
|
+
partial = load_cached_partial(path, context)
|
|
118
|
+
|
|
119
|
+
context.stack do
|
|
120
|
+
context["include"] = parse_params(context) if @params
|
|
121
|
+
begin
|
|
122
|
+
partial.render!(context)
|
|
123
|
+
rescue Liquid::Error => e
|
|
124
|
+
e.template_name = path
|
|
125
|
+
e.markup_context = "included " if e.markup_context.nil?
|
|
126
|
+
raise e
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def add_include_to_dependency(site, path, context)
|
|
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
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def load_cached_partial(path, context)
|
|
141
|
+
context.registers[:cached_partials] ||= {}
|
|
142
|
+
cached_partial = context.registers[:cached_partials]
|
|
143
|
+
|
|
144
|
+
if cached_partial.key?(path)
|
|
145
|
+
cached_partial[path]
|
|
146
|
+
else
|
|
147
|
+
unparsed_file = context.registers[:site]
|
|
148
|
+
.liquid_renderer
|
|
149
|
+
.file(path)
|
|
150
|
+
begin
|
|
151
|
+
cached_partial[path] = unparsed_file.parse(read_file(path, context))
|
|
152
|
+
rescue Liquid::Error => e
|
|
153
|
+
e.template_name = path
|
|
154
|
+
e.markup_context = "included " if e.markup_context.nil?
|
|
155
|
+
raise e
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def valid_include_file?(path, dir, safe)
|
|
161
|
+
!outside_site_source?(path, dir, safe) && File.file?(path)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def outside_site_source?(path, dir, safe)
|
|
165
|
+
safe && !realpath_prefixed_with?(path, dir)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def realpath_prefixed_with?(path, dir)
|
|
169
|
+
File.exist?(path) && File.realpath(path).start_with?(dir)
|
|
170
|
+
rescue StandardError
|
|
171
|
+
false
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# This method allows to modify the file content by inheriting from the class.
|
|
175
|
+
def read_file(file, context)
|
|
176
|
+
File.read(file, **file_read_opts(context))
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
def could_not_locate_message(file, includes_dirs, safe)
|
|
182
|
+
message = "Could not locate the included file '#{file}' in any of "\
|
|
183
|
+
"#{includes_dirs}. Ensure it exists in one of those directories and"
|
|
184
|
+
message + if safe
|
|
185
|
+
" is not a symlink as those are not allowed in safe mode."
|
|
186
|
+
else
|
|
187
|
+
", if it is a symlink, does not point outside your site source."
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
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
|
+
|
|
251
|
+
class IncludeRelativeTag < IncludeTag
|
|
252
|
+
def tag_includes_dirs(context)
|
|
253
|
+
Array(page_path(context)).freeze
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def page_path(context)
|
|
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.sub(%r!/#excerpt\z!, "")
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
|
|
275
|
+
Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
|
data/lib/jekyll/tags/link.rb
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jekyll
|
|
4
|
-
module Tags
|
|
5
|
-
class Link < Liquid::Tag
|
|
6
|
-
include Jekyll::Filters::URLFilters
|
|
7
|
-
|
|
8
|
-
class << self
|
|
9
|
-
def tag_name
|
|
10
|
-
name.split("::").last.downcase
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def initialize(tag_name, relative_path, tokens)
|
|
15
|
-
super
|
|
16
|
-
|
|
17
|
-
@relative_path = relative_path.strip
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def render(context)
|
|
21
|
-
@context = context
|
|
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)
|
|
25
|
-
|
|
26
|
-
site.each_site_file do |item|
|
|
27
|
-
return relative_url(item) if item.relative_path == relative_path
|
|
28
|
-
# This takes care of the case for static files that have a leading /
|
|
29
|
-
return relative_url(item) if item.relative_path == relative_path_with_leading_slash
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
raise ArgumentError, <<~MSG
|
|
33
|
-
Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'.
|
|
34
|
-
|
|
35
|
-
Make sure the document exists and the path is correct.
|
|
36
|
-
MSG
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
Liquid::Template.register_tag(Jekyll::Tags::Link.tag_name, Jekyll::Tags::Link)
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Tags
|
|
5
|
+
class Link < Liquid::Tag
|
|
6
|
+
include Jekyll::Filters::URLFilters
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def tag_name
|
|
10
|
+
name.split("::").last.downcase
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(tag_name, relative_path, tokens)
|
|
15
|
+
super
|
|
16
|
+
|
|
17
|
+
@relative_path = relative_path.strip
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(context)
|
|
21
|
+
@context = context
|
|
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)
|
|
25
|
+
|
|
26
|
+
site.each_site_file do |item|
|
|
27
|
+
return relative_url(item) if item.relative_path == relative_path
|
|
28
|
+
# This takes care of the case for static files that have a leading /
|
|
29
|
+
return relative_url(item) if item.relative_path == relative_path_with_leading_slash
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
raise ArgumentError, <<~MSG
|
|
33
|
+
Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'.
|
|
34
|
+
|
|
35
|
+
Make sure the document exists and the path is correct.
|
|
36
|
+
MSG
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Liquid::Template.register_tag(Jekyll::Tags::Link.tag_name, Jekyll::Tags::Link)
|