jekyll 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +48 -19
- data/lib/jekyll.rb +3 -0
- data/lib/jekyll/collection.rb +1 -1
- data/lib/jekyll/command.rb +4 -2
- data/lib/jekyll/commands/new.rb +2 -2
- data/lib/jekyll/commands/serve.rb +9 -1
- data/lib/jekyll/configuration.rb +1 -1
- data/lib/jekyll/converters/identity.rb +2 -2
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -1
- data/lib/jekyll/convertible.rb +15 -15
- data/lib/jekyll/document.rb +18 -4
- data/lib/jekyll/drops/document_drop.rb +12 -0
- data/lib/jekyll/drops/page_drop.rb +18 -0
- data/lib/jekyll/drops/url_drop.rb +8 -0
- data/lib/jekyll/entry_filter.rb +19 -6
- data/lib/jekyll/excerpt.rb +1 -1
- data/lib/jekyll/filters.rb +99 -14
- data/lib/jekyll/filters/url_filters.rb +41 -14
- data/lib/jekyll/frontmatter_defaults.rb +12 -17
- data/lib/jekyll/hooks.rb +2 -5
- data/lib/jekyll/inclusion.rb +32 -0
- data/lib/jekyll/liquid_renderer.rb +18 -15
- data/lib/jekyll/liquid_renderer/table.rb +1 -21
- data/lib/jekyll/page.rb +43 -0
- data/lib/jekyll/page_excerpt.rb +26 -0
- data/lib/jekyll/profiler.rb +58 -0
- data/lib/jekyll/readers/collection_reader.rb +1 -0
- data/lib/jekyll/readers/data_reader.rb +1 -0
- data/lib/jekyll/readers/layout_reader.rb +1 -0
- data/lib/jekyll/readers/page_reader.rb +1 -0
- data/lib/jekyll/readers/post_reader.rb +1 -0
- data/lib/jekyll/readers/static_file_reader.rb +1 -0
- data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
- data/lib/jekyll/renderer.rb +9 -15
- data/lib/jekyll/site.rb +14 -5
- data/lib/jekyll/static_file.rb +14 -9
- data/lib/jekyll/tags/include.rb +58 -3
- data/lib/jekyll/theme.rb +6 -0
- data/lib/jekyll/utils.rb +4 -4
- data/lib/jekyll/utils/win_tz.rb +1 -1
- data/lib/jekyll/version.rb +1 -1
- data/lib/theme_template/theme.gemspec.erb +1 -4
- metadata +14 -31
data/lib/jekyll/document.rb
CHANGED
@@ -116,7 +116,7 @@ module Jekyll
|
|
116
116
|
#
|
117
117
|
# Returns the output extension
|
118
118
|
def output_ext
|
119
|
-
|
119
|
+
renderer.output_ext
|
120
120
|
end
|
121
121
|
|
122
122
|
# The base filename of the document, without the file extname.
|
@@ -133,6 +133,10 @@ module Jekyll
|
|
133
133
|
@basename ||= File.basename(path)
|
134
134
|
end
|
135
135
|
|
136
|
+
def renderer
|
137
|
+
@renderer ||= Jekyll::Renderer.new(site, self)
|
138
|
+
end
|
139
|
+
|
136
140
|
# Produces a "cleaned" relative path.
|
137
141
|
# The "cleaned" relative path is the relative path without the extname
|
138
142
|
# and with the collection's directory removed as well.
|
@@ -414,9 +418,13 @@ module Jekyll
|
|
414
418
|
#
|
415
419
|
# Returns nothing.
|
416
420
|
def categories_from_path(special_dir)
|
417
|
-
|
418
|
-
|
419
|
-
|
421
|
+
if relative_path.start_with?(special_dir)
|
422
|
+
superdirs = []
|
423
|
+
else
|
424
|
+
superdirs = relative_path.sub(Document.superdirs_regex(special_dir), "")
|
425
|
+
superdirs = superdirs.split(File::SEPARATOR)
|
426
|
+
superdirs.reject! { |c| c.empty? || c == special_dir || c == basename }
|
427
|
+
end
|
420
428
|
|
421
429
|
merge_data!({ "categories" => superdirs }, :source => "file path")
|
422
430
|
end
|
@@ -490,6 +498,7 @@ module Jekyll
|
|
490
498
|
end
|
491
499
|
end
|
492
500
|
|
501
|
+
# rubocop:disable Metrics/AbcSize
|
493
502
|
def populate_title
|
494
503
|
if relative_path =~ DATE_FILENAME_MATCHER
|
495
504
|
date, slug, ext = Regexp.last_match.captures
|
@@ -497,6 +506,10 @@ module Jekyll
|
|
497
506
|
elsif relative_path =~ DATELESS_FILENAME_MATCHER
|
498
507
|
slug, ext = Regexp.last_match.captures
|
499
508
|
end
|
509
|
+
# `slug` will be nil for documents without an extension since the regex patterns
|
510
|
+
# above tests for an extension as well.
|
511
|
+
# In such cases, assign `basename_without_ext` as the slug.
|
512
|
+
slug ||= basename_without_ext
|
500
513
|
|
501
514
|
# slugs shouldn't end with a period
|
502
515
|
# `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
|
@@ -508,6 +521,7 @@ module Jekyll
|
|
508
521
|
data["slug"] ||= slug
|
509
522
|
data["ext"] ||= ext
|
510
523
|
end
|
524
|
+
# rubocop:enable Metrics/AbcSize
|
511
525
|
|
512
526
|
def modify_date(date)
|
513
527
|
if !data["date"] || data["date"].to_i == site.time.to_i
|
@@ -64,6 +64,18 @@ module Jekyll
|
|
64
64
|
result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
68
|
+
def title
|
69
|
+
@obj.data["title"]
|
70
|
+
end
|
71
|
+
|
72
|
+
def categories
|
73
|
+
@obj.data["categories"]
|
74
|
+
end
|
75
|
+
|
76
|
+
def tags
|
77
|
+
@obj.data["tags"]
|
78
|
+
end
|
67
79
|
end
|
68
80
|
end
|
69
81
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module Drops
|
5
|
+
class PageDrop < Drop
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
mutable false
|
9
|
+
|
10
|
+
def_delegators :@obj, :content, :dir, :name, :path, :url, :excerpt
|
11
|
+
private def_delegator :@obj, :data, :fallback_data
|
12
|
+
|
13
|
+
def title
|
14
|
+
@obj.data["title"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -35,6 +35,14 @@ module Jekyll
|
|
35
35
|
category_set.to_a.join("/")
|
36
36
|
end
|
37
37
|
|
38
|
+
# Similar to output from #categories, but each category will be downcased and
|
39
|
+
# all non-alphanumeric characters of the category replaced with a hyphen.
|
40
|
+
def slugified_categories
|
41
|
+
Array(@obj.data["categories"]).each_with_object(Set.new) do |category, set|
|
42
|
+
set << Utils.slugify(category.to_s)
|
43
|
+
end.to_a.join("/")
|
44
|
+
end
|
45
|
+
|
38
46
|
# CCYY
|
39
47
|
def year
|
40
48
|
@obj.date.strftime("%Y")
|
data/lib/jekyll/entry_filter.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class EntryFilter
|
5
5
|
attr_reader :site
|
6
|
+
|
6
7
|
SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze
|
7
8
|
|
8
9
|
def initialize(site, base_directory = nil)
|
@@ -27,20 +28,30 @@ module Jekyll
|
|
27
28
|
)
|
28
29
|
end
|
29
30
|
|
31
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
30
32
|
def filter(entries)
|
31
33
|
entries.reject do |e|
|
32
34
|
# Reject this entry if it is just a "dot" representation.
|
33
35
|
# e.g.: '.', '..', '_movies/.', 'music/..', etc
|
34
36
|
next true if e.end_with?(".")
|
35
|
-
|
37
|
+
|
38
|
+
# Check if the current entry is explicitly included and cache the result
|
39
|
+
included = included?(e)
|
40
|
+
|
41
|
+
# Reject current entry if it is excluded but not explicitly included as well.
|
42
|
+
next true if excluded?(e) && !included
|
43
|
+
|
44
|
+
# Reject current entry if it is a symlink.
|
36
45
|
next true if symlink?(e)
|
37
|
-
# Do not reject this entry if it is included.
|
38
|
-
next false if included?(e)
|
39
46
|
|
40
|
-
#
|
41
|
-
|
47
|
+
# Do not reject current entry if it is explicitly included.
|
48
|
+
next false if included
|
49
|
+
|
50
|
+
# Reject current entry if it is special or a backup file.
|
51
|
+
special?(e) || backup?(e)
|
42
52
|
end
|
43
53
|
end
|
54
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
44
55
|
|
45
56
|
def included?(entry)
|
46
57
|
glob_include?(site.include, entry) ||
|
@@ -91,6 +102,7 @@ module Jekyll
|
|
91
102
|
# Returns true if path matches against any glob pattern, else false.
|
92
103
|
def glob_include?(enumerator, entry)
|
93
104
|
entry_with_source = PathManager.join(site.source, entry)
|
105
|
+
entry_is_directory = File.directory?(entry_with_source)
|
94
106
|
|
95
107
|
enumerator.any? do |pattern|
|
96
108
|
case pattern
|
@@ -98,7 +110,8 @@ module Jekyll
|
|
98
110
|
pattern_with_source = PathManager.join(site.source, pattern)
|
99
111
|
|
100
112
|
File.fnmatch?(pattern_with_source, entry_with_source) ||
|
101
|
-
entry_with_source.start_with?(pattern_with_source)
|
113
|
+
entry_with_source.start_with?(pattern_with_source) ||
|
114
|
+
(pattern_with_source == "#{entry_with_source}/" if entry_is_directory)
|
102
115
|
when Regexp
|
103
116
|
pattern.match?(entry_with_source)
|
104
117
|
else
|
data/lib/jekyll/excerpt.rb
CHANGED
@@ -56,7 +56,7 @@ module Jekyll
|
|
56
56
|
#
|
57
57
|
# Returns true if the string passed in
|
58
58
|
def include?(something)
|
59
|
-
|
59
|
+
output&.include?(something) || content.include?(something)
|
60
60
|
end
|
61
61
|
|
62
62
|
# The UID for this doc (useful in feeds).
|
data/lib/jekyll/filters.rb
CHANGED
@@ -121,8 +121,20 @@ module Jekyll
|
|
121
121
|
# input - The String on which to operate.
|
122
122
|
#
|
123
123
|
# Returns the Integer word count.
|
124
|
-
def number_of_words(input)
|
125
|
-
|
124
|
+
def number_of_words(input, mode = nil)
|
125
|
+
cjk_charset = '\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}'
|
126
|
+
cjk_regex = %r![#{cjk_charset}]!o
|
127
|
+
word_regex = %r![^#{cjk_charset}\s]+!o
|
128
|
+
|
129
|
+
case mode
|
130
|
+
when "cjk"
|
131
|
+
input.scan(cjk_regex).length + input.scan(word_regex).length
|
132
|
+
when "auto"
|
133
|
+
cjk_count = input.scan(cjk_regex).length
|
134
|
+
cjk_count.zero? ? input.split.length : cjk_count + input.scan(word_regex).length
|
135
|
+
else
|
136
|
+
input.split.length
|
137
|
+
end
|
126
138
|
end
|
127
139
|
|
128
140
|
# Join an array of things into a string by separating with commas and the
|
@@ -210,6 +222,66 @@ module Jekyll
|
|
210
222
|
end || []
|
211
223
|
end
|
212
224
|
|
225
|
+
# Search an array of objects and returns the first object that has the queried attribute
|
226
|
+
# with the given value or returns nil otherwise.
|
227
|
+
#
|
228
|
+
# input - the object array.
|
229
|
+
# property - the property within each object to search by.
|
230
|
+
# value - the desired value.
|
231
|
+
# Cannot be an instance of Array nor Hash since calling #to_s on them returns
|
232
|
+
# their `#inspect` string object.
|
233
|
+
#
|
234
|
+
# Returns the found object or nil
|
235
|
+
#
|
236
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
237
|
+
def find(input, property, value)
|
238
|
+
return input if !property || value.is_a?(Array) || value.is_a?(Hash)
|
239
|
+
return input unless input.respond_to?(:find)
|
240
|
+
|
241
|
+
input = input.values if input.is_a?(Hash)
|
242
|
+
input_id = input.hash
|
243
|
+
|
244
|
+
# implement a hash based on method parameters to cache the end-result for given parameters.
|
245
|
+
@find_filter_cache ||= {}
|
246
|
+
@find_filter_cache[input_id] ||= {}
|
247
|
+
@find_filter_cache[input_id][property] ||= {}
|
248
|
+
|
249
|
+
# stash or retrive results to return
|
250
|
+
# Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>"
|
251
|
+
# to validate caching.
|
252
|
+
result = @find_filter_cache[input_id][property][value] ||= begin
|
253
|
+
input.find do |object|
|
254
|
+
compare_property_vs_target(item_property(object, property), value)
|
255
|
+
end || "<__NO MATCH__>"
|
256
|
+
end
|
257
|
+
return nil if result == "<__NO MATCH__>"
|
258
|
+
|
259
|
+
result
|
260
|
+
end
|
261
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
262
|
+
|
263
|
+
# Searches an array of objects against an expression and returns the first object for which
|
264
|
+
# the expression evaluates to true, or returns nil otherwise.
|
265
|
+
#
|
266
|
+
# input - the object array
|
267
|
+
# variable - the variable to assign each item to in the expression
|
268
|
+
# expression - a Liquid comparison expression passed in as a string
|
269
|
+
#
|
270
|
+
# Returns the found object or nil
|
271
|
+
def find_exp(input, variable, expression)
|
272
|
+
return input unless input.respond_to?(:find)
|
273
|
+
|
274
|
+
input = input.values if input.is_a?(Hash)
|
275
|
+
|
276
|
+
condition = parse_condition(expression)
|
277
|
+
@context.stack do
|
278
|
+
input.find do |object|
|
279
|
+
@context[variable] = object
|
280
|
+
condition.evaluate(@context)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
213
285
|
# Convert the input into integer
|
214
286
|
#
|
215
287
|
# input - the object string
|
@@ -356,15 +428,24 @@ module Jekyll
|
|
356
428
|
@item_property_cache ||= {}
|
357
429
|
@item_property_cache[property] ||= {}
|
358
430
|
@item_property_cache[property][item] ||= begin
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
431
|
+
property = property.to_s
|
432
|
+
property = if item.respond_to?(:to_liquid)
|
433
|
+
read_liquid_attribute(item.to_liquid, property)
|
434
|
+
elsif item.respond_to?(:data)
|
435
|
+
item.data[property]
|
436
|
+
else
|
437
|
+
item[property]
|
438
|
+
end
|
439
|
+
|
440
|
+
parse_sort_input(property)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def read_liquid_attribute(liquid_data, property)
|
445
|
+
return liquid_data[property] unless property.include?(".")
|
446
|
+
|
447
|
+
property.split(".").reduce(liquid_data) do |data, key|
|
448
|
+
data.respond_to?(:[]) && data[key]
|
368
449
|
end
|
369
450
|
end
|
370
451
|
|
@@ -423,10 +504,14 @@ module Jekyll
|
|
423
504
|
#
|
424
505
|
# Returns an instance of Liquid::Condition
|
425
506
|
def parse_binary_comparison(parser)
|
426
|
-
parse_comparison(parser)
|
427
|
-
|
428
|
-
|
507
|
+
condition = parse_comparison(parser)
|
508
|
+
first_condition = condition
|
509
|
+
while (binary_operator = parser.id?("and") || parser.id?("or"))
|
510
|
+
child_condition = parse_comparison(parser)
|
511
|
+
condition.send(binary_operator, child_condition)
|
512
|
+
condition = child_condition
|
429
513
|
end
|
514
|
+
first_condition
|
430
515
|
end
|
431
516
|
|
432
517
|
# Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
|
@@ -11,15 +11,16 @@ module Jekyll
|
|
11
11
|
def absolute_url(input)
|
12
12
|
return if input.nil?
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
cache = if input.is_a?(String)
|
15
|
+
(@context.registers[:site].filter_cache[:absolute_url] ||= {})
|
16
|
+
else
|
17
|
+
(@context.registers[:cached_absolute_url] ||= {})
|
18
|
+
end
|
19
|
+
cache[input] ||= compute_absolute_url(input)
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
# Duplicate cached string so that the cached value is never mutated by
|
22
|
+
# a subsequent filter.
|
23
|
+
cache[input].dup
|
23
24
|
end
|
24
25
|
|
25
26
|
# Produces a URL relative to the domain root based on site.baseurl
|
@@ -31,13 +32,16 @@ module Jekyll
|
|
31
32
|
def relative_url(input)
|
32
33
|
return if input.nil?
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
cache = if input.is_a?(String)
|
36
|
+
(@context.registers[:site].filter_cache[:relative_url] ||= {})
|
37
|
+
else
|
38
|
+
(@context.registers[:cached_relative_url] ||= {})
|
39
|
+
end
|
40
|
+
cache[input] ||= compute_relative_url(input)
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
).normalize.to_s
|
42
|
+
# Duplicate cached string so that the cached value is never mutated by
|
43
|
+
# a subsequent filter.
|
44
|
+
cache[input].dup
|
41
45
|
end
|
42
46
|
|
43
47
|
# Strips trailing `/index.html` from URLs to create pretty permalinks
|
@@ -53,6 +57,29 @@ module Jekyll
|
|
53
57
|
|
54
58
|
private
|
55
59
|
|
60
|
+
def compute_absolute_url(input)
|
61
|
+
input = input.url if input.respond_to?(:url)
|
62
|
+
return input if Addressable::URI.parse(input.to_s).absolute?
|
63
|
+
|
64
|
+
site = @context.registers[:site]
|
65
|
+
site_url = site.config["url"]
|
66
|
+
return relative_url(input) if site_url.nil? || site_url == ""
|
67
|
+
|
68
|
+
Addressable::URI.parse(
|
69
|
+
site_url.to_s + relative_url(input)
|
70
|
+
).normalize.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
def compute_relative_url(input)
|
74
|
+
input = input.url if input.respond_to?(:url)
|
75
|
+
return input if Addressable::URI.parse(input.to_s).absolute?
|
76
|
+
|
77
|
+
parts = [sanitized_baseurl, input]
|
78
|
+
Addressable::URI.parse(
|
79
|
+
parts.compact.map { |part| ensure_leading_slash(part.to_s) }.join
|
80
|
+
).normalize.to_s
|
81
|
+
end
|
82
|
+
|
56
83
|
def sanitized_baseurl
|
57
84
|
site = @context.registers[:site]
|
58
85
|
site.config["baseurl"].to_s.chomp("/")
|
@@ -103,15 +103,15 @@ module Jekyll
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def applies_path?(scope, path)
|
106
|
-
|
106
|
+
rel_scope_path = scope["path"]
|
107
|
+
return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty?
|
107
108
|
|
108
|
-
sanitized_path =
|
109
|
-
rel_scope_path = Pathname.new(scope["path"])
|
109
|
+
sanitized_path = sanitize_path(path)
|
110
110
|
|
111
|
-
if
|
111
|
+
if rel_scope_path.include?("*")
|
112
112
|
glob_scope(sanitized_path, rel_scope_path)
|
113
113
|
else
|
114
|
-
path_is_subpath?(sanitized_path, strip_collections_dir(
|
114
|
+
path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -134,11 +134,7 @@ module Jekyll
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def path_is_subpath?(path, parent_path)
|
137
|
-
path.
|
138
|
-
return true if ascended_path.to_s == parent_path.to_s
|
139
|
-
end
|
140
|
-
|
141
|
-
false
|
137
|
+
path.start_with?(parent_path)
|
142
138
|
end
|
143
139
|
|
144
140
|
def strip_collections_dir(path)
|
@@ -179,7 +175,7 @@ module Jekyll
|
|
179
175
|
# new_scope - the new scope hash
|
180
176
|
#
|
181
177
|
# Returns true if the new scope has precedence over the older
|
182
|
-
# rubocop: disable PredicateName
|
178
|
+
# rubocop: disable Naming/PredicateName
|
183
179
|
def has_precedence?(old_scope, new_scope)
|
184
180
|
return true if old_scope.nil?
|
185
181
|
|
@@ -194,7 +190,7 @@ module Jekyll
|
|
194
190
|
!old_scope.key? "type"
|
195
191
|
end
|
196
192
|
end
|
197
|
-
# rubocop: enable PredicateName
|
193
|
+
# rubocop: enable Naming/PredicateName
|
198
194
|
|
199
195
|
# Collects a list of sets that match the given path and type
|
200
196
|
#
|
@@ -230,15 +226,14 @@ module Jekyll
|
|
230
226
|
end.compact
|
231
227
|
end
|
232
228
|
|
233
|
-
# Sanitizes the given path by removing a leading
|
234
|
-
|
235
|
-
SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z!.freeze
|
236
|
-
|
229
|
+
# Sanitizes the given path by removing a leading slash
|
237
230
|
def sanitize_path(path)
|
238
231
|
if path.nil? || path.empty?
|
239
232
|
""
|
233
|
+
elsif path.start_with?("/")
|
234
|
+
path.gsub(%r!\A/|(?<=[^/])\z!, "")
|
240
235
|
else
|
241
|
-
path
|
236
|
+
path
|
242
237
|
end
|
243
238
|
end
|
244
239
|
end
|