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.
- checksums.yaml +4 -4
- data/.rubocop.yml +511 -89
- data/LICENSE +1 -1
- data/README.markdown +48 -27
- data/lib/blank_template/_config.yml +3 -0
- data/lib/blank_template/_layouts/default.html +12 -0
- data/lib/blank_template/_sass/base.scss +9 -0
- data/lib/blank_template/assets/css/main.scss +4 -0
- data/lib/blank_template/index.md +8 -0
- data/lib/jekyll/cache.rb +186 -0
- data/lib/jekyll/cleaner.rb +8 -7
- data/lib/jekyll/collection.rb +84 -11
- data/lib/jekyll/command.rb +33 -6
- data/lib/jekyll/commands/build.rb +8 -28
- data/lib/jekyll/commands/clean.rb +3 -2
- data/lib/jekyll/commands/doctor.rb +46 -35
- data/lib/jekyll/commands/help.rb +1 -1
- data/lib/jekyll/commands/new.rb +44 -50
- data/lib/jekyll/commands/new_theme.rb +27 -28
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +9 -16
- data/lib/jekyll/commands/serve/servlet.rb +21 -22
- data/lib/jekyll/commands/serve/websockets.rb +1 -1
- data/lib/jekyll/commands/serve.rb +75 -97
- data/lib/jekyll/configuration.rb +66 -158
- data/lib/jekyll/converters/identity.rb +18 -0
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +83 -33
- data/lib/jekyll/converters/markdown.rb +49 -40
- data/lib/jekyll/converters/smartypants.rb +34 -14
- data/lib/jekyll/convertible.rb +36 -34
- data/lib/jekyll/deprecator.rb +2 -4
- data/lib/jekyll/document.rb +107 -72
- data/lib/jekyll/drops/collection_drop.rb +3 -4
- data/lib/jekyll/drops/document_drop.rb +9 -3
- data/lib/jekyll/drops/drop.rb +115 -33
- data/lib/jekyll/drops/excerpt_drop.rb +8 -0
- data/lib/jekyll/drops/site_drop.rb +9 -8
- data/lib/jekyll/drops/static_file_drop.rb +4 -4
- data/lib/jekyll/drops/theme_drop.rb +39 -0
- data/lib/jekyll/drops/unified_payload_drop.rb +7 -2
- data/lib/jekyll/drops/url_drop.rb +55 -3
- data/lib/jekyll/entry_filter.rb +42 -51
- data/lib/jekyll/excerpt.rb +48 -38
- data/lib/jekyll/external.rb +20 -19
- data/lib/jekyll/filters/date_filters.rb +6 -3
- data/lib/jekyll/filters/grouping_filters.rb +1 -2
- data/lib/jekyll/filters/url_filters.rb +50 -15
- data/lib/jekyll/filters.rb +211 -50
- data/lib/jekyll/frontmatter_defaults.rb +45 -36
- data/lib/jekyll/hooks.rb +26 -26
- data/lib/jekyll/inclusion.rb +32 -0
- data/lib/jekyll/layout.rb +12 -19
- data/lib/jekyll/liquid_extensions.rb +0 -2
- data/lib/jekyll/liquid_renderer/file.rb +24 -3
- data/lib/jekyll/liquid_renderer/table.rb +26 -77
- data/lib/jekyll/liquid_renderer.rb +31 -16
- data/lib/jekyll/log_adapter.rb +5 -1
- data/lib/jekyll/page.rb +51 -23
- data/lib/jekyll/page_excerpt.rb +25 -0
- data/lib/jekyll/page_without_a_file.rb +0 -4
- data/lib/jekyll/path_manager.rb +74 -0
- data/lib/jekyll/plugin.rb +5 -11
- data/lib/jekyll/plugin_manager.rb +15 -5
- data/lib/jekyll/profiler.rb +51 -0
- data/lib/jekyll/reader.rb +65 -10
- data/lib/jekyll/readers/collection_reader.rb +1 -0
- data/lib/jekyll/readers/data_reader.rb +48 -10
- data/lib/jekyll/readers/layout_reader.rb +3 -12
- data/lib/jekyll/readers/page_reader.rb +5 -5
- data/lib/jekyll/readers/post_reader.rb +32 -19
- data/lib/jekyll/readers/static_file_reader.rb +4 -4
- data/lib/jekyll/readers/theme_assets_reader.rb +8 -5
- data/lib/jekyll/regenerator.rb +4 -12
- data/lib/jekyll/related_posts.rb +1 -1
- data/lib/jekyll/renderer.rb +34 -49
- data/lib/jekyll/site.rb +151 -58
- data/lib/jekyll/static_file.rb +64 -28
- data/lib/jekyll/stevenson.rb +4 -8
- data/lib/jekyll/tags/highlight.rb +44 -57
- data/lib/jekyll/tags/include.rb +114 -80
- data/lib/jekyll/tags/link.rb +12 -7
- data/lib/jekyll/tags/post_url.rb +33 -30
- data/lib/jekyll/theme.rb +20 -18
- data/lib/jekyll/theme_builder.rb +91 -89
- data/lib/jekyll/url.rb +18 -10
- data/lib/jekyll/utils/ansi.rb +2 -2
- data/lib/jekyll/utils/exec.rb +0 -1
- data/lib/jekyll/utils/internet.rb +2 -4
- data/lib/jekyll/utils/platforms.rb +37 -52
- data/lib/jekyll/utils/thread_event.rb +1 -5
- data/lib/jekyll/utils.rb +29 -28
- data/lib/jekyll/version.rb +1 -1
- data/lib/jekyll.rb +9 -14
- data/lib/site_template/.gitignore +2 -0
- data/lib/site_template/404.html +2 -1
- data/lib/site_template/_config.yml +17 -5
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
- data/lib/theme_template/README.md.erb +1 -3
- data/lib/theme_template/gitignore.erb +1 -0
- data/lib/theme_template/theme.gemspec.erb +1 -4
- data/rubocop/jekyll/assert_equal_literal_actual.rb +150 -0
- data/rubocop/jekyll/no_p_allowed.rb +5 -6
- data/rubocop/jekyll/no_puts_allowed.rb +5 -6
- metadata +149 -37
- data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
- data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
- data/lib/jekyll/utils/rouge.rb +0 -22
- /data/lib/site_template/{about.md → about.markdown} +0 -0
- /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,
|
|
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
|
-
|
|
24
|
+
#{markup}
|
|
25
25
|
|
|
26
|
-
Valid syntax: highlight <lang> [linenos]
|
|
27
|
-
|
|
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(
|
|
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
|
|
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,
|
|
90
|
-
Jekyll
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
|
|
90
|
+
formatter.format(lexer.lex(code))
|
|
91
|
+
end
|
|
101
92
|
|
|
102
|
-
|
|
93
|
+
def line_highlighter_formatter(formatter)
|
|
94
|
+
Rouge::Formatters::HTMLLineHighlighter.new(
|
|
95
|
+
formatter,
|
|
96
|
+
:highlight_lines => mark_lines
|
|
97
|
+
)
|
|
98
|
+
end
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
104
|
+
raise SyntaxError, "Syntax Error for mark_lines declaration. Expected a " \
|
|
105
|
+
"double-quoted list of integers."
|
|
112
106
|
end
|
|
113
107
|
|
|
114
|
-
def
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
data/lib/jekyll/tags/include.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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[
|
|
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
|
|
67
|
-
raise ArgumentError,
|
|
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
|
-
|
|
58
|
+
#{file}
|
|
71
59
|
|
|
72
|
-
Valid syntax:
|
|
60
|
+
Valid syntax:
|
|
73
61
|
|
|
74
|
-
|
|
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
|
|
82
|
-
raise ArgumentError,
|
|
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
|
-
|
|
73
|
+
#{@params}
|
|
86
74
|
|
|
87
|
-
Valid syntax:
|
|
75
|
+
Valid syntax:
|
|
88
76
|
|
|
89
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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::
|
|
274
|
+
Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
|
|
241
275
|
Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
|
data/lib/jekyll/tags/link.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
29
|
+
return relative_url(item) if item.relative_path == relative_path_with_leading_slash
|
|
25
30
|
end
|
|
26
31
|
|
|
27
|
-
raise ArgumentError,
|
|
28
|
-
Could not find document '#{
|
|
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
|
data/lib/jekyll/tags/post_url.rb
CHANGED
|
@@ -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
|
-
|
|
16
|
+
"'#{name}' does not contain valid date and/or title."
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
@name_regex = %r!^_posts/#{path}#{
|
|
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(
|
|
26
|
-
|
|
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
|
|
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,
|
|
64
|
-
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
|
|
65
|
-
|
|
66
|
-
|
|
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 |
|
|
77
|
-
return
|
|
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 |
|
|
84
|
-
next unless @post.deprecated_equality
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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
|