jekyll 3.7.4 → 3.8.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b691b6dcd0dc56026736d18a916692f32aab72f8797725bc684624ebfe4cc4b2
4
- data.tar.gz: 2c7b61a0c25f6acb41e19e066992b841efe049e8528993da12724b6c02c2d43f
3
+ metadata.gz: 3d8b3cb33faecac197557dfc43ba3a5e6b24053ecc42c9bb9ea3a733553f7eb4
4
+ data.tar.gz: 3af6b03a6c04a694adac3507d1e82048046d791ce6254d1b827fccea97db4367
5
5
  SHA512:
6
- metadata.gz: dc23c577bd77f53fd39e36182607d56ececd41e14d703faf0b88f720b6a8cdc44e5c89b97ab7dc1c7d91b382c0250c3fcab1ea35f160ccc04b520a9579eea7f2
7
- data.tar.gz: 844a42fcb469bcfa6aaca552ce4a7cdfc2bf7669734420f763ea9e2f03a24f2a2630efc999c122572c5fd041157dfaa201bb5d6bf9f966494d204355d5b945ba
6
+ metadata.gz: 6d2dbd4a413268950af60bb1cb3219bb48a6fcdaefdc76e0035e97fb20852759d6d293b0b1a10bb39ab40e8527befaec5f63d028f01056fabf6c88fe9e4e99a4
7
+ data.tar.gz: dc435a723327de90ff63432a53db4fca2f11e2240c1f661fc66f6871610f737aa9eb2947a7434dbddd148443afb69e01c17044104bf9a185ddbdff4697b41fec
@@ -57,9 +57,9 @@ module Jekyll
57
57
  #
58
58
  # Returns a Set with the file paths
59
59
  def new_files
60
- files = Set.new
61
- site.each_site_file { |item| files << item.destination(site.dest) }
62
- files
60
+ @new_files ||= Set.new.tap do |files|
61
+ site.each_site_file { |item| files << item.destination(site.dest) }
62
+ end
63
63
  end
64
64
 
65
65
  # Private: The list of directories to be created when site is built.
@@ -67,7 +67,7 @@ module Jekyll
67
67
  #
68
68
  # Returns a Set with the directory paths
69
69
  def new_dirs
70
- new_files.map { |file| parent_dirs(file) }.flatten.to_set
70
+ @new_dirs ||= new_files.map { |file| parent_dirs(file) }.flatten.to_set
71
71
  end
72
72
 
73
73
  # Private: The list of parent directories of a given file
@@ -210,12 +210,11 @@ module Jekyll
210
210
  private
211
211
 
212
212
  def read_document(full_path)
213
- doc = Jekyll::Document.new(full_path, :site => site, :collection => self)
214
- doc.read
215
- if site.publisher.publish?(doc) || !write?
216
- docs << doc
217
- else
218
- Jekyll.logger.debug "Skipped Publishing:", doc.relative_path
213
+ docs << Document.new(full_path, :site => site, :collection => self).tap do |doc|
214
+ doc.read
215
+ if !site.publisher.publish?(doc) && site.publisher.hidden_in_the_future?(doc)
216
+ Jekyll.logger.debug "Skip Publishing:", "#{doc.relative_path} has a future date"
217
+ end
219
218
  end
220
219
  end
221
220
 
@@ -41,9 +41,22 @@ module Jekyll
41
41
  !conflicting_urls(site),
42
42
  !urls_only_differ_by_case(site),
43
43
  proper_site_url?(site),
44
+ properly_gathered_posts?(site),
44
45
  ].all?
45
46
  end
46
47
 
48
+ def properly_gathered_posts?(site)
49
+ return true if site.config["collections_dir"].empty?
50
+ posts_at_root = site.in_source_dir("_posts")
51
+ return true unless File.directory?(posts_at_root)
52
+ Jekyll.logger.warn "Warning:",
53
+ "Detected '_posts' directory outside custom `collections_dir`!"
54
+ Jekyll.logger.warn "",
55
+ "Please move '#{posts_at_root}' into the custom directory at " \
56
+ "'#{site.in_source_dir(site.config["collections_dir"])}'"
57
+ false
58
+ end
59
+
47
60
  def deprecated_relative_permalinks(site)
48
61
  if site.config["relative_permalinks"]
49
62
  Jekyll::Deprecator.deprecation_message "Your site still uses relative" \
@@ -27,8 +27,9 @@ module Jekyll
27
27
  new_blog_path = File.expand_path(args.join(" "), Dir.pwd)
28
28
  FileUtils.mkdir_p new_blog_path
29
29
  if preserve_source_location?(new_blog_path, options)
30
- Jekyll.logger.abort_with "Conflict:",
31
- "#{new_blog_path} exists and is not empty."
30
+ Jekyll.logger.error "Conflict:", "#{new_blog_path} exists and is not empty."
31
+ Jekyll.logger.abort_with "", "Ensure #{new_blog_path} is empty or else " \
32
+ "try again with `--force` to proceed and overwrite any files."
32
33
  end
33
34
 
34
35
  if options["blank"]
@@ -61,7 +61,9 @@ module Jekyll
61
61
  "defaults" => [],
62
62
 
63
63
  "liquid" => {
64
- "error_mode" => "warn",
64
+ "error_mode" => "warn",
65
+ "strict_filters" => false,
66
+ "strict_variables" => false,
65
67
  },
66
68
 
67
69
  "rdiscount" => {
@@ -165,9 +165,9 @@ module Jekyll
165
165
 
166
166
  # Determine whether the file should be rendered with Liquid.
167
167
  #
168
- # Always returns true.
168
+ # Returns true if the file has Liquid Tags or Variables, false otherwise.
169
169
  def render_with_liquid?
170
- true
170
+ Jekyll::Utils.has_liquid_construct?(content)
171
171
  end
172
172
 
173
173
  # Determine whether the file should be placed into layouts.
@@ -227,6 +227,7 @@ module Jekyll
227
227
  def write(dest)
228
228
  path = destination(dest)
229
229
  FileUtils.mkdir_p(File.dirname(path))
230
+ Jekyll.logger.debug "Writing:", path
230
231
  File.write(path, output, :mode => "wb")
231
232
  Jekyll::Hooks.trigger hook_owner, :post_write, self
232
233
  end
@@ -83,15 +83,14 @@ module Jekyll
83
83
  # Returns a String path which represents the relative path from the collections_dir
84
84
  # to this document.
85
85
  def relative_path
86
- @relative_path ||=
87
- Pathutil.new(path).relative_path_from(site.collections_path).to_s
86
+ @relative_path ||= path.sub("#{site.collections_path}/", "")
88
87
  end
89
88
 
90
89
  # The output extension of the document.
91
90
  #
92
91
  # Returns the output extension
93
92
  def output_ext
94
- Jekyll::Renderer.new(site, self).output_ext
93
+ @output_ext ||= Jekyll::Renderer.new(site, self).output_ext
95
94
  end
96
95
 
97
96
  # The base filename of the document, without the file extname.
@@ -157,9 +156,10 @@ module Jekyll
157
156
  # Determine whether the file should be rendered with Liquid.
158
157
  #
159
158
  # Returns false if the document is either an asset file or a yaml file,
159
+ # or if the document doesn't contain any Liquid Tags or Variables,
160
160
  # true otherwise.
161
161
  def render_with_liquid?
162
- !(coffeescript_file? || yaml_file?)
162
+ !(coffeescript_file? || yaml_file? || !Utils.has_liquid_construct?(content))
163
163
  end
164
164
 
165
165
  # Determine whether the file should be rendered with a layout.
@@ -239,6 +239,7 @@ module Jekyll
239
239
  def write(dest)
240
240
  path = destination(dest)
241
241
  FileUtils.mkdir_p(File.dirname(path))
242
+ Jekyll.logger.debug "Writing:", path
242
243
  File.write(path, output, :mode => "wb")
243
244
 
244
245
  trigger_hooks(:post_write)
@@ -311,9 +312,10 @@ module Jekyll
311
312
  # Based on the Collection to which it belongs.
312
313
  #
313
314
  # True if the document has a collection and if that collection's #write?
314
- # method returns true, otherwise false.
315
+ # method returns true, and if the site's Publisher will publish the document.
316
+ # False otherwise.
315
317
  def write?
316
- collection && collection.write?
318
+ collection && collection.write? && site.publisher.publish?(self)
317
319
  end
318
320
 
319
321
  # The Document excerpt_separator, from the YAML Front-Matter or site
@@ -358,7 +360,7 @@ module Jekyll
358
360
  #
359
361
  # Returns an Array of related Posts.
360
362
  def related_posts
361
- Jekyll::RelatedPosts.new(self).build
363
+ @related_posts ||= Jekyll::RelatedPosts.new(self).build
362
364
  end
363
365
 
364
366
  # Override of normal respond_to? to match method_missing's logic for
@@ -417,7 +419,7 @@ module Jekyll
417
419
  def merge_categories!(other)
418
420
  if other.key?("categories") && !other["categories"].nil?
419
421
  if other["categories"].is_a?(String)
420
- other["categories"] = other["categories"].split(%r!\s+!).map(&:strip)
422
+ other["categories"] = other["categories"].split
421
423
  end
422
424
  other["categories"] = (data["categories"] || []) | other["categories"]
423
425
  end
@@ -11,12 +11,11 @@ module Jekyll
11
11
  def_delegators :@obj, :label, :docs, :files, :directory,
12
12
  :relative_directory
13
13
 
14
+ private def_delegator :@obj, :metadata, :fallback_data
15
+
14
16
  def to_s
15
17
  docs.to_s
16
18
  end
17
-
18
- private
19
- def_delegator :@obj, :metadata, :fallback_data
20
19
  end
21
20
  end
22
21
  end
@@ -14,6 +14,8 @@ module Jekyll
14
14
  def_delegator :@obj, :relative_path, :path
15
15
  def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
16
16
 
17
+ private def_delegator :@obj, :data, :fallback_data
18
+
17
19
  def collection
18
20
  @obj.collection.label
19
21
  end
@@ -61,9 +63,6 @@ module Jekyll
61
63
  result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
62
64
  end
63
65
  end
64
-
65
- private
66
- def_delegator :@obj, :data, :fallback_data
67
66
  end
68
67
  end
69
68
  end
@@ -11,6 +11,8 @@ module Jekyll
11
11
  def_delegators :@obj, :time, :pages, :static_files, :documents,
12
12
  :tags, :categories
13
13
 
14
+ private def_delegator :@obj, :config, :fallback_data
15
+
14
16
  def [](key)
15
17
  if @obj.collections.key?(key) && key != "posts"
16
18
  @obj.collections[key].docs
@@ -37,11 +39,18 @@ module Jekyll
37
39
  @site_collections ||= @obj.collections.values.sort_by(&:label).map(&:to_liquid)
38
40
  end
39
41
 
42
+ # `{{ site.related_posts }}` is how posts can get posts related to
43
+ # them, either through LSI if it's enabled, or through the most
44
+ # recent posts.
45
+ # We should remove this in 4.0 and switch to `{{ post.related_posts }}`.
46
+ def related_posts
47
+ return nil unless @current_document.is_a?(Jekyll::Document)
48
+ @current_document.related_posts
49
+ end
50
+ attr_writer :current_document
51
+
40
52
  # return nil for `{{ site.config }}` even if --config was passed via CLI
41
53
  def config; end
42
-
43
- private
44
- def_delegator :@obj, :config, :fallback_data
45
54
  end
46
55
  end
47
56
  end
@@ -6,8 +6,9 @@ module Jekyll
6
6
  extend Forwardable
7
7
  def_delegators :@obj, :name, :extname, :modified_time, :basename
8
8
  def_delegator :@obj, :relative_path, :path
9
- def_delegator :@obj, :data, :fallback_data
10
9
  def_delegator :@obj, :type, :collection
10
+
11
+ private def_delegator :@obj, :data, :fallback_data
11
12
  end
12
13
  end
13
14
  end
@@ -31,12 +31,9 @@ module Jekyll
31
31
 
32
32
  def filter(entries)
33
33
  entries.reject do |e|
34
- # Reject this entry if it is a symlink.
35
- next true if symlink?(e)
36
- # Do not reject this entry if it is included.
37
- next false if included?(e)
38
- # Reject this entry if it is special, a backup file, or excluded.
39
- special?(e) || backup?(e) || excluded?(e)
34
+ unless included?(e)
35
+ special?(e) || backup?(e) || excluded?(e) || symlink?(e)
36
+ end
40
37
  end
41
38
  end
42
39
 
@@ -8,7 +8,7 @@ module Jekyll
8
8
  attr_accessor :content, :ext
9
9
  attr_writer :output
10
10
 
11
- def_delegators :@doc, :site, :name, :ext, :relative_path, :extname,
11
+ def_delegators :@doc, :site, :name, :ext, :extname,
12
12
  :render_with_liquid?, :collection, :related_posts,
13
13
  :url, :next_doc, :previous_doc
14
14
 
@@ -41,6 +41,13 @@ module Jekyll
41
41
  File.join(doc.path, "#excerpt")
42
42
  end
43
43
 
44
+ # 'Relative Path' of the excerpt.
45
+ #
46
+ # Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
47
+ def relative_path
48
+ File.join(doc.relative_path, "#excerpt")
49
+ end
50
+
44
51
  # Check if excerpt includes a string
45
52
  #
46
53
  # Returns true if the string passed in
@@ -116,11 +123,40 @@ module Jekyll
116
123
  def extract_excerpt(doc_content)
117
124
  head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
118
125
 
126
+ # append appropriate closing tag (to a Liquid block), to the "head" if the
127
+ # partitioning resulted in leaving the closing tag somewhere in the "tail"
128
+ # partition.
129
+ if head.include?("{%")
130
+ head =~ %r!{%\s*(\w+).+\s*%}!
131
+ tag_name = Regexp.last_match(1)
132
+
133
+ if liquid_block?(tag_name) && head.match(%r!{%\s*end#{tag_name}\s*%}!).nil?
134
+ print_build_warning
135
+ head << "\n{% end#{tag_name} %}"
136
+ end
137
+ end
138
+
119
139
  if tail.empty?
120
140
  head
121
141
  else
122
142
  head.to_s.dup << "\n\n" << tail.scan(%r!^ {0,3}\[[^\]]+\]:.+$!).join("\n")
123
143
  end
124
144
  end
145
+
146
+ private
147
+
148
+ def liquid_block?(tag_name)
149
+ Liquid::Template.tags[tag_name].superclass == Liquid::Block
150
+ end
151
+
152
+ def print_build_warning
153
+ Jekyll.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
154
+ Jekyll.logger.warn "",
155
+ "Found a Liquid block containing separator '#{doc.excerpt_separator}' and has " \
156
+ "been modified with the appropriate closing tag."
157
+ Jekyll.logger.warn "",
158
+ "Feel free to define a custom excerpt or excerpt_separator in the document's " \
159
+ "Front Matter if the generated excerpt is unsatisfactory."
160
+ end
125
161
  end
126
162
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "addressable/uri"
4
4
  require "json"
5
- require "date"
6
5
  require "liquid"
7
6
 
8
7
  require_all "jekyll/filters"
@@ -11,6 +10,7 @@ module Jekyll
11
10
  module Filters
12
11
  include URLFilters
13
12
  include GroupingFilters
13
+ include DateFilters
14
14
 
15
15
  # Convert a Markdown string into HTML output.
16
16
  #
@@ -67,55 +67,6 @@ module Jekyll
67
67
  Utils.slugify(input, :mode => mode)
68
68
  end
69
69
 
70
- # Format a date in short format e.g. "27 Jan 2011".
71
- #
72
- # date - the Time to format.
73
- #
74
- # Returns the formatting String.
75
- def date_to_string(date)
76
- time(date).strftime("%d %b %Y")
77
- end
78
-
79
- # Format a date in long format e.g. "27 January 2011".
80
- #
81
- # date - The Time to format.
82
- #
83
- # Returns the formatted String.
84
- def date_to_long_string(date)
85
- return date if date.to_s.empty?
86
- time(date).strftime("%d %B %Y")
87
- end
88
-
89
- # Format a date for use in XML.
90
- #
91
- # date - The Time to format.
92
- #
93
- # Examples
94
- #
95
- # date_to_xmlschema(Time.now)
96
- # # => "2011-04-24T20:34:46+08:00"
97
- #
98
- # Returns the formatted String.
99
- def date_to_xmlschema(date)
100
- return date if date.to_s.empty?
101
- time(date).xmlschema
102
- end
103
-
104
- # Format a date according to RFC-822
105
- #
106
- # date - The Time to format.
107
- #
108
- # Examples
109
- #
110
- # date_to_rfc822(Time.now)
111
- # # => "Sun, 24 Apr 2011 12:34:46 +0000"
112
- #
113
- # Returns the formatted String.
114
- def date_to_rfc822(date)
115
- return date if date.to_s.empty?
116
- time(date).rfc822
117
- end
118
-
119
70
  # XML escape a string for use. Replaces any special characters with
120
71
  # appropriate HTML entity replacements.
121
72
  #
@@ -223,7 +174,7 @@ module Jekyll
223
174
  return input unless input.respond_to?(:select)
224
175
  input = input.values if input.is_a?(Hash)
225
176
  input.select do |object|
226
- Array(item_property(object, property)).map(&:to_s).include?(value.to_s)
177
+ Array(item_property(object, property)).map!(&:to_s).include?(value.to_s)
227
178
  end || []
228
179
  end
229
180
 
@@ -357,16 +308,6 @@ module Jekyll
357
308
  .map!(&:last)
358
309
  end
359
310
 
360
- private
361
- def time(input)
362
- date = Liquid::Utils.to_date(input)
363
- unless date.respond_to?(:to_time)
364
- raise Errors::InvalidDateError,
365
- "Invalid Date: '#{input.inspect}' is not a valid datetime."
366
- end
367
- date.to_time.dup.localtime
368
- end
369
-
370
311
  private
371
312
  def item_property(item, property)
372
313
  if item.respond_to?(:to_liquid)
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module DateFilters
6
+ # Format a date in short format e.g. "27 Jan 2011".
7
+ # Ordinal format is also supported, in both the UK
8
+ # (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats.
9
+ # UK format is the default.
10
+ #
11
+ # date - the Time to format.
12
+ # type - if "ordinal" the returned String will be in ordinal format
13
+ # style - if "US" the returned String will be in US format.
14
+ # Otherwise it will be in UK format.
15
+ #
16
+ # Returns the formatting String.
17
+ def date_to_string(date, type = nil, style = nil)
18
+ stringify_date(date, "%b", type, style)
19
+ end
20
+
21
+ # Format a date in long format e.g. "27 January 2011".
22
+ # Ordinal format is also supported, in both the UK
23
+ # (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats.
24
+ # UK format is the default.
25
+ #
26
+ # date - the Time to format.
27
+ # type - if "ordinal" the returned String will be in ordinal format
28
+ # style - if "US" the returned String will be in US format.
29
+ # Otherwise it will be in UK format.
30
+ #
31
+ # Returns the formatted String.
32
+ def date_to_long_string(date, type = nil, style = nil)
33
+ stringify_date(date, "%B", type, style)
34
+ end
35
+
36
+ # Format a date for use in XML.
37
+ #
38
+ # date - The Time to format.
39
+ #
40
+ # Examples
41
+ #
42
+ # date_to_xmlschema(Time.now)
43
+ # # => "2011-04-24T20:34:46+08:00"
44
+ #
45
+ # Returns the formatted String.
46
+ def date_to_xmlschema(date)
47
+ return date if date.to_s.empty?
48
+ time(date).xmlschema
49
+ end
50
+
51
+ # Format a date according to RFC-822
52
+ #
53
+ # date - The Time to format.
54
+ #
55
+ # Examples
56
+ #
57
+ # date_to_rfc822(Time.now)
58
+ # # => "Sun, 24 Apr 2011 12:34:46 +0000"
59
+ #
60
+ # Returns the formatted String.
61
+ def date_to_rfc822(date)
62
+ return date if date.to_s.empty?
63
+ time(date).rfc822
64
+ end
65
+
66
+ private
67
+ # month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
68
+ # type: nil (default) or "ordinal"
69
+ # style: nil (default) or "US"
70
+ #
71
+ # Returns a stringified date or the empty input.
72
+ def stringify_date(date, month_type, type = nil, style = nil)
73
+ return date if date.to_s.empty?
74
+ time = time(date)
75
+ if type == "ordinal"
76
+ day = time.day
77
+ ordinal_day = "#{day}#{ordinal(day)}"
78
+ return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
79
+ return time.strftime("#{ordinal_day} #{month_type} %Y")
80
+ end
81
+ time.strftime("%d #{month_type} %Y")
82
+ end
83
+
84
+ private
85
+ def ordinal(number)
86
+ return "th" if (11..13).cover?(number)
87
+
88
+ case number % 10
89
+ when 1 then "st"
90
+ when 2 then "nd"
91
+ when 3 then "rd"
92
+ else "th"
93
+ end
94
+ end
95
+
96
+ private
97
+ def time(input)
98
+ date = Liquid::Utils.to_date(input)
99
+ unless date.respond_to?(:to_time)
100
+ raise Errors::InvalidDateError,
101
+ "Invalid Date: '#{input.inspect}' is not a valid datetime."
102
+ end
103
+ date.to_time.dup.localtime
104
+ end
105
+ end
106
+ end
107
+ end
@@ -5,6 +5,11 @@ require_relative "liquid_renderer/table"
5
5
 
6
6
  module Jekyll
7
7
  class LiquidRenderer
8
+ extend Forwardable
9
+
10
+ def_delegator :@site, :in_source_dir, :source_dir
11
+ def_delegator :@site, :in_theme_dir, :theme_dir
12
+
8
13
  def initialize(site)
9
14
  @site = site
10
15
  Liquid::Template.error_mode = @site.config["liquid"]["error_mode"].to_sym
@@ -16,11 +21,13 @@ module Jekyll
16
21
  end
17
22
 
18
23
  def file(filename)
19
- filename = @site.in_source_dir(filename).sub(
20
- %r!\A#{Regexp.escape(@site.source)}/!,
21
- ""
22
- )
23
-
24
+ filename.match(filename_regex)
25
+ filename =
26
+ if Regexp.last_match(1) == theme_dir("")
27
+ ::File.join(Regexp.last_match(1).split("/")[-1], Regexp.last_match(2))
28
+ else
29
+ Regexp.last_match(2)
30
+ end
24
31
  LiquidRenderer::File.new(self, filename).tap do
25
32
  @stats[filename] ||= new_profile_hash
26
33
  @stats[filename][:count] += 1
@@ -44,6 +51,11 @@ module Jekyll
44
51
  end
45
52
 
46
53
  private
54
+
55
+ def filename_regex
56
+ %r!\A(#{source_dir}/|#{theme_dir}/|\W*)(.*)!oi
57
+ end
58
+
47
59
  def new_profile_hash
48
60
  Hash.new { |hash, key| hash[key] = 0 }
49
61
  end
@@ -63,8 +63,8 @@ module Jekyll
63
63
  # Returns nothing.
64
64
  def retrieve_posts(dir)
65
65
  return if outside_configured_directory?(dir)
66
- site.posts.docs.concat(PostReader.new(site).read_posts(dir))
67
- site.posts.docs.concat(PostReader.new(site).read_drafts(dir)) if site.show_drafts
66
+ site.posts.docs.concat(post_reader.read_posts(dir))
67
+ site.posts.docs.concat(post_reader.read_drafts(dir)) if site.show_drafts
68
68
  end
69
69
 
70
70
  # Recursively traverse directories with the read_directories function.
@@ -78,7 +78,7 @@ module Jekyll
78
78
  dot_dirs.each do |file|
79
79
  dir_path = site.in_source_dir(dir, file)
80
80
  rel_path = File.join(dir, file)
81
- unless @site.dest.sub(%r!/$!, "") == dir_path
81
+ unless @site.dest.chomp("/") == dir_path
82
82
  @site.reader.read_directories(rel_path)
83
83
  end
84
84
  end
@@ -146,5 +146,11 @@ module Jekyll
146
146
  collections_dir = site.config["collections_dir"]
147
147
  !collections_dir.empty? && !dir.start_with?("/#{collections_dir}")
148
148
  end
149
+
150
+ # Create a single PostReader instance to retrieve drafts and posts from all valid
151
+ # directories in current site.
152
+ def post_reader
153
+ @post_reader ||= PostReader.new(site)
154
+ end
149
155
  end
150
156
  end
@@ -120,6 +120,7 @@ module Jekyll
120
120
  # Returns nothing.
121
121
  def write_metadata
122
122
  unless disabled?
123
+ Jekyll.logger.debug "Writing Metadata:", ".jekyll-metadata"
123
124
  File.binwrite(metadata_file, Marshal.dump(metadata))
124
125
  end
125
126
  end
@@ -46,7 +46,7 @@ module Jekyll
46
46
  end
47
47
 
48
48
  def most_recent_posts
49
- @most_recent_posts ||= (site.posts.docs.reverse - [post]).first(10)
49
+ @most_recent_posts ||= (site.posts.docs.last(11).reverse - [post]).first(10)
50
50
  end
51
51
  end
52
52
  end
@@ -52,7 +52,7 @@ module Jekyll
52
52
  Jekyll.logger.debug "Rendering:", document.relative_path
53
53
 
54
54
  assign_pages!
55
- assign_related_posts!
55
+ assign_current_document!
56
56
  assign_highlighter_options!
57
57
  assign_layout_data!
58
58
 
@@ -68,8 +68,11 @@ module Jekyll
68
68
  # rubocop: disable AbcSize
69
69
  def render_document
70
70
  info = {
71
- :registers => { :site => site, :page => payload["page"] },
71
+ :registers => { :site => site, :page => payload["page"] },
72
+ :strict_filters => liquid_options["strict_filters"],
73
+ :strict_variables => liquid_options["strict_variables"],
72
74
  }
75
+
73
76
  output = document.content
74
77
  if document.render_with_liquid?
75
78
  Jekyll.logger.debug "Rendering Liquid:", document.relative_path
@@ -77,7 +80,7 @@ module Jekyll
77
80
  end
78
81
 
79
82
  Jekyll.logger.debug "Rendering Markup:", document.relative_path
80
- output = convert(output)
83
+ output = convert(output.to_s)
81
84
  document.content = output
82
85
 
83
86
  if document.place_in_layout?
@@ -169,12 +172,16 @@ module Jekyll
169
172
  # Returns nothing
170
173
  private
171
174
  def validate_layout(layout)
172
- return unless invalid_layout?(layout)
173
- Jekyll.logger.warn(
174
- "Build Warning:",
175
- "Layout '#{document.data["layout"]}' requested "\
176
- "in #{document.relative_path} does not exist."
177
- )
175
+ if invalid_layout?(layout)
176
+ Jekyll.logger.warn(
177
+ "Build Warning:",
178
+ "Layout '#{document.data["layout"]}' requested "\
179
+ "in #{document.relative_path} does not exist."
180
+ )
181
+ elsif !layout.nil?
182
+ layout_source = layout.path.start_with?(site.source) ? :site : :theme
183
+ Jekyll.logger.debug "Layout source:", layout_source
184
+ end
178
185
  end
179
186
 
180
187
  # Render layout content into document.output
@@ -217,12 +224,8 @@ module Jekyll
217
224
  #
218
225
  # Returns nothing
219
226
  private
220
- def assign_related_posts!
221
- if document.is_a?(Document) && document.collection.label == "posts"
222
- payload["site"]["related_posts"] = document.related_posts
223
- else
224
- payload["site"]["related_posts"] = nil
225
- end
227
+ def assign_current_document!
228
+ payload["site"].current_document = document
226
229
  end
227
230
 
228
231
  # Set highlighter prefix and suffix
@@ -244,8 +247,9 @@ module Jekyll
244
247
 
245
248
  private
246
249
  def permalink_ext
247
- if document.permalink && !document.permalink.end_with?("/")
248
- permalink_ext = File.extname(document.permalink)
250
+ document_permalink = document.permalink
251
+ if document_permalink && !document_permalink.end_with?("/")
252
+ permalink_ext = File.extname(document_permalink)
249
253
  permalink_ext unless permalink_ext.empty?
250
254
  end
251
255
  end
@@ -265,5 +269,10 @@ module Jekyll
265
269
  c.output_ext(document.extname)
266
270
  end.compact
267
271
  end
272
+
273
+ private
274
+ def liquid_options
275
+ @liquid_options ||= site.config["liquid"]
276
+ end
268
277
  end
269
278
  end
@@ -93,9 +93,12 @@ module Jekyll
93
93
  self.pages = []
94
94
  self.static_files = []
95
95
  self.data = {}
96
+ @site_data = nil
96
97
  @collections = nil
98
+ @docs_to_write = nil
97
99
  @regenerator.clear_cache
98
100
  @liquid_renderer.reset
101
+ @site_cleaner = nil
99
102
 
100
103
  if limit_posts < 0
101
104
  raise ArgumentError, "limit_posts must be a non-negative number"
@@ -252,7 +255,7 @@ module Jekyll
252
255
  #
253
256
  # Returns the Hash to be hooked to site.data.
254
257
  def site_data
255
- config["data"] || data
258
+ @site_data ||= (config["data"] || data)
256
259
  end
257
260
 
258
261
  # The Hash payload containing site-wide data.
@@ -311,7 +314,7 @@ module Jekyll
311
314
  #
312
315
  # Returns an Array of Documents which should be written
313
316
  def docs_to_write
314
- documents.select(&:write?)
317
+ @docs_to_write ||= documents.select(&:write?)
315
318
  end
316
319
 
317
320
  # Get all the documents
@@ -458,10 +461,7 @@ module Jekyll
458
461
  def render_docs(payload)
459
462
  collections.each_value do |collection|
460
463
  collection.docs.each do |document|
461
- if regenerator.regenerate?(document)
462
- document.output = Jekyll::Renderer.new(self, document, payload).run
463
- document.trigger_hooks(:post_render)
464
- end
464
+ render_regenerated(document, payload)
465
465
  end
466
466
  end
467
467
  end
@@ -469,11 +469,15 @@ module Jekyll
469
469
  private
470
470
  def render_pages(payload)
471
471
  pages.flatten.each do |page|
472
- if regenerator.regenerate?(page)
473
- page.output = Jekyll::Renderer.new(self, page, payload).run
474
- page.trigger_hooks(:post_render)
475
- end
472
+ render_regenerated(page, payload)
476
473
  end
477
474
  end
475
+
476
+ private
477
+ def render_regenerated(document, payload)
478
+ return unless regenerator.regenerate?(document)
479
+ document.output = Jekyll::Renderer.new(self, document, payload).run
480
+ document.trigger_hooks(:post_render)
481
+ end
478
482
  end
479
483
  end
@@ -137,7 +137,7 @@ module Jekyll
137
137
  :template => @collection.url_template,
138
138
  :placeholders => placeholders,
139
139
  })
140
- end.to_s.gsub(%r!/$!, "")
140
+ end.to_s.chomp("/")
141
141
  end
142
142
 
143
143
  # Returns the type of the collection if present, nil otherwise.
@@ -19,7 +19,11 @@ module Jekyll
19
19
  VARIABLE_SYNTAX = %r!
20
20
  (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
21
21
  (?<params>.*)
22
- !x
22
+ !mx
23
+
24
+ FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
25
+ VALID_FILENAME_CHARS = %r!^[\w/\.-]+$!
26
+ INVALID_SEQUENCES = %r![./]{2,}!
23
27
 
24
28
  def initialize(tag_name, markup, tokens)
25
29
  super
@@ -59,7 +63,7 @@ module Jekyll
59
63
  end
60
64
 
61
65
  def validate_file_name(file)
62
- if file !~ %r!^[a-zA-Z0-9_/\.-]+$! || file =~ %r!\./! || file =~ %r!/\.!
66
+ if file =~ INVALID_SEQUENCES || file !~ VALID_FILENAME_CHARS
63
67
  raise ArgumentError, <<-MSG
64
68
  Invalid syntax for include tag. File contains invalid characters or sequences:
65
69
 
@@ -74,8 +78,7 @@ MSG
74
78
  end
75
79
 
76
80
  def validate_params
77
- full_valid_syntax = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!
78
- unless @params =~ full_valid_syntax
81
+ unless @params =~ FULL_VALID_SYNTAX
79
82
  raise ArgumentError, <<-MSG
80
83
  Invalid syntax for include tag:
81
84
 
@@ -96,7 +99,7 @@ MSG
96
99
 
97
100
  # Render the variable if required
98
101
  def render_variable(context)
99
- if @file.match(VARIABLE_SYNTAX)
102
+ if @file =~ VARIABLE_SYNTAX
100
103
  partial = context.registers[:site]
101
104
  .liquid_renderer
102
105
  .file("(variable)")
@@ -8,6 +8,8 @@ module Jekyll
8
8
 
9
9
  def initialize(name)
10
10
  @name = name.downcase.strip
11
+ Jekyll.logger.debug "Theme:", name
12
+ Jekyll.logger.debug "Theme source:", root
11
13
  configure_sass
12
14
  end
13
15
 
@@ -21,19 +23,19 @@ module Jekyll
21
23
  end
22
24
 
23
25
  def includes_path
24
- path_for "_includes".freeze
26
+ @includes_path ||= path_for "_includes".freeze
25
27
  end
26
28
 
27
29
  def layouts_path
28
- path_for "_layouts".freeze
30
+ @layouts_path ||= path_for "_layouts".freeze
29
31
  end
30
32
 
31
33
  def sass_path
32
- path_for "_sass".freeze
34
+ @sass_path ||= path_for "_sass".freeze
33
35
  end
34
36
 
35
37
  def assets_path
36
- path_for "assets".freeze
38
+ @assets_path ||= path_for "assets".freeze
37
39
  end
38
40
 
39
41
  def configure_sass
@@ -56,6 +58,7 @@ module Jekyll
56
58
  def realpath_for(folder)
57
59
  File.realpath(Jekyll.sanitized_path(root, folder.to_s))
58
60
  rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
61
+ Jekyll.logger.warn "Invalid theme folder:", folder
59
62
  nil
60
63
  end
61
64
 
@@ -8,7 +8,7 @@ class Jekyll::ThemeBuilder
8
8
  attr_reader :name, :path, :code_of_conduct
9
9
 
10
10
  def initialize(theme_name, opts)
11
- @name = theme_name.to_s.tr(" ", "_").gsub(%r!_+!, "_")
11
+ @name = theme_name.to_s.tr(" ", "_").squeeze("_")
12
12
  @path = Pathname.new(File.expand_path(name, Dir.pwd))
13
13
  @code_of_conduct = !!opts["code_of_conduct"]
14
14
  end
@@ -72,9 +72,9 @@ module Jekyll
72
72
  break result if result.index(":").nil?
73
73
  if token.last.nil?
74
74
  # Remove leading "/" to avoid generating urls with `//`
75
- result.gsub(%r!/:#{token.first}!, "")
75
+ result.gsub("/:#{token.first}", "")
76
76
  else
77
- result.gsub(%r!:#{token.first}!, self.class.escape_path(token.last))
77
+ result.gsub(":#{token.first}", self.class.escape_path(token.last))
78
78
  end
79
79
  end
80
80
  end
@@ -109,14 +109,14 @@ module Jekyll
109
109
  replacement = self.class.escape_path(value)
110
110
 
111
111
  match.sub(":#{winner}", replacement)
112
- end.gsub(%r!//!, "/".freeze)
112
+ end.squeeze("/")
113
113
  end
114
114
 
115
115
  # Returns a sanitized String URL, stripping "../../" and multiples of "/",
116
116
  # as well as the beginning "/" so we can enforce and ensure it.
117
117
 
118
118
  def sanitize_url(str)
119
- "/" + str.gsub(%r!/{2,}!, "/").gsub(%r!\.+/|\A/+!, "")
119
+ "/#{str}".gsub("..", "/").gsub("./", "").squeeze("/")
120
120
  end
121
121
 
122
122
  # Escapes a path to be a valid URL path segment
@@ -147,6 +147,14 @@ module Jekyll
147
147
  rescue EOFError
148
148
  false
149
149
  end
150
+
151
+ # Determine whether the given content string contains Liquid Tags or Vaiables
152
+ #
153
+ # Returns true is the string contains sequences of `{%` or `{{`
154
+ def has_liquid_construct?(content)
155
+ return false if content.nil? || content.empty?
156
+ content.include?("{%") || content.include?("{{")
157
+ end
150
158
  # rubocop: enable PredicateName
151
159
 
152
160
  # Slugify a filename or title.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jekyll
4
- VERSION = "3.7.4".freeze
4
+ VERSION = "3.8.0.pre.rc1".freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.7.4
4
+ version: 3.8.0.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-07 00:00:00.000000000 Z
11
+ date: 2018-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -237,6 +237,7 @@ files:
237
237
  - lib/jekyll/excerpt.rb
238
238
  - lib/jekyll/external.rb
239
239
  - lib/jekyll/filters.rb
240
+ - lib/jekyll/filters/date_filters.rb
240
241
  - lib/jekyll/filters/grouping_filters.rb
241
242
  - lib/jekyll/filters/url_filters.rb
242
243
  - lib/jekyll/frontmatter_defaults.rb
@@ -322,12 +323,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
322
323
  version: 2.1.0
323
324
  required_rubygems_version: !ruby/object:Gem::Requirement
324
325
  requirements:
325
- - - ">="
326
+ - - ">"
326
327
  - !ruby/object:Gem::Version
327
- version: '0'
328
+ version: 1.3.1
328
329
  requirements: []
329
330
  rubyforge_project:
330
- rubygems_version: 2.7.6
331
+ rubygems_version: 2.7.3
331
332
  signing_key:
332
333
  specification_version: 2
333
334
  summary: A simple, blog aware, static site generator.