jekyll 4.0.0.pre.alpha1 → 4.1.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +58 -17
  3. data/lib/jekyll.rb +5 -1
  4. data/lib/jekyll/cache.rb +71 -64
  5. data/lib/jekyll/cleaner.rb +5 -5
  6. data/lib/jekyll/collection.rb +6 -4
  7. data/lib/jekyll/command.rb +4 -2
  8. data/lib/jekyll/commands/new.rb +4 -4
  9. data/lib/jekyll/commands/serve.rb +9 -1
  10. data/lib/jekyll/commands/serve/servlet.rb +13 -14
  11. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  12. data/lib/jekyll/configuration.rb +40 -129
  13. data/lib/jekyll/converters/identity.rb +2 -2
  14. data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -9
  15. data/lib/jekyll/convertible.rb +21 -20
  16. data/lib/jekyll/document.rb +56 -31
  17. data/lib/jekyll/drops/document_drop.rb +12 -0
  18. data/lib/jekyll/drops/drop.rb +14 -8
  19. data/lib/jekyll/drops/site_drop.rb +11 -1
  20. data/lib/jekyll/drops/url_drop.rb +52 -1
  21. data/lib/jekyll/entry_filter.rb +38 -44
  22. data/lib/jekyll/excerpt.rb +3 -3
  23. data/lib/jekyll/filters.rb +140 -22
  24. data/lib/jekyll/filters/url_filters.rb +41 -14
  25. data/lib/jekyll/frontmatter_defaults.rb +15 -20
  26. data/lib/jekyll/hooks.rb +2 -5
  27. data/lib/jekyll/inclusion.rb +32 -0
  28. data/lib/jekyll/liquid_renderer.rb +18 -15
  29. data/lib/jekyll/liquid_renderer/file.rb +10 -0
  30. data/lib/jekyll/liquid_renderer/table.rb +18 -61
  31. data/lib/jekyll/mime.types +53 -11
  32. data/lib/jekyll/page.rb +26 -2
  33. data/lib/jekyll/page_excerpt.rb +25 -0
  34. data/lib/jekyll/path_manager.rb +31 -0
  35. data/lib/jekyll/profiler.rb +58 -0
  36. data/lib/jekyll/reader.rb +4 -1
  37. data/lib/jekyll/readers/collection_reader.rb +1 -0
  38. data/lib/jekyll/readers/data_reader.rb +1 -0
  39. data/lib/jekyll/readers/layout_reader.rb +1 -0
  40. data/lib/jekyll/readers/page_reader.rb +5 -5
  41. data/lib/jekyll/readers/post_reader.rb +2 -1
  42. data/lib/jekyll/readers/static_file_reader.rb +3 -3
  43. data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
  44. data/lib/jekyll/renderer.rb +9 -15
  45. data/lib/jekyll/site.rb +21 -12
  46. data/lib/jekyll/static_file.rb +15 -10
  47. data/lib/jekyll/tags/highlight.rb +2 -4
  48. data/lib/jekyll/tags/include.rb +67 -11
  49. data/lib/jekyll/tags/post_url.rb +8 -5
  50. data/lib/jekyll/theme.rb +19 -10
  51. data/lib/jekyll/url.rb +7 -3
  52. data/lib/jekyll/utils.rb +14 -18
  53. data/lib/jekyll/utils/platforms.rb +1 -1
  54. data/lib/jekyll/utils/win_tz.rb +1 -1
  55. data/lib/jekyll/version.rb +1 -1
  56. data/lib/theme_template/theme.gemspec.erb +1 -4
  57. metadata +33 -36
@@ -4,7 +4,7 @@ module Jekyll
4
4
  class StaticFile
5
5
  extend Forwardable
6
6
 
7
- attr_reader :relative_path, :extname, :name, :data
7
+ attr_reader :relative_path, :extname, :name
8
8
 
9
9
  def_delegator :to_liquid, :to_json, :to_json
10
10
 
@@ -25,7 +25,7 @@ module Jekyll
25
25
  # base - The String path to the <source>.
26
26
  # dir - The String path between <source> and the file.
27
27
  # name - The String filename of the file.
28
- # rubocop: disable ParameterLists
28
+ # rubocop: disable Metrics/ParameterLists
29
29
  def initialize(site, base, dir, name, collection = nil)
30
30
  @site = site
31
31
  @base = base
@@ -34,17 +34,18 @@ module Jekyll
34
34
  @collection = collection
35
35
  @relative_path = File.join(*[@dir, @name].compact)
36
36
  @extname = File.extname(@name)
37
- @data = @site.frontmatter_defaults.all(relative_path, type)
38
37
  end
39
- # rubocop: enable ParameterLists
38
+ # rubocop: enable Metrics/ParameterLists
40
39
 
41
40
  # Returns source file path.
42
41
  def path
43
- # Static file is from a collection inside custom collections directory
44
- if !@collection.nil? && !@site.config["collections_dir"].empty?
45
- File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
46
- else
47
- File.join(*[@base, @dir, @name].compact)
42
+ @path ||= begin
43
+ # Static file is from a collection inside custom collections directory
44
+ if !@collection.nil? && !@site.config["collections_dir"].empty?
45
+ File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
46
+ else
47
+ File.join(*[@base, @dir, @name].compact)
48
+ end
48
49
  end
49
50
  end
50
51
 
@@ -111,6 +112,10 @@ module Jekyll
111
112
  true
112
113
  end
113
114
 
115
+ def data
116
+ @data ||= @site.frontmatter_defaults.all(relative_path, type)
117
+ end
118
+
114
119
  def to_liquid
115
120
  @to_liquid ||= Drops::StaticFileDrop.new(self)
116
121
  end
@@ -126,7 +131,7 @@ module Jekyll
126
131
  :collection => @collection.label,
127
132
  :path => cleaned_relative_path,
128
133
  :output_ext => "",
129
- :name => "",
134
+ :name => basename,
130
135
  :title => "",
131
136
  }
132
137
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Jekyll
4
4
  module Tags
5
- class HighlightBlock < Liquid::Raw
5
+ class HighlightBlock < Liquid::Block
6
6
  include Liquid::StandardFilters
7
7
 
8
8
  # The regular expression syntax checker. Start with the language specifier.
@@ -33,7 +33,7 @@ module Jekyll
33
33
  def render(context)
34
34
  prefix = context["highlighter_prefix"] || ""
35
35
  suffix = context["highlighter_suffix"] || ""
36
- code = @body.gsub(LEADING_OR_TRAILING_LINE_TERMINATORS, "")
36
+ code = super.to_s.gsub(LEADING_OR_TRAILING_LINE_TERMINATORS, "")
37
37
 
38
38
  output =
39
39
  case context.registers[:site].highlighter
@@ -103,8 +103,6 @@ module Jekyll
103
103
  "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
104
104
  "#{code.chomp}</code></pre></figure>"
105
105
  end
106
-
107
- def ensure_valid_markup(tag_name, markup, parse_context); end
108
106
  end
109
107
  end
110
108
  end
@@ -5,25 +5,26 @@ module Jekyll
5
5
  class IncludeTag < Liquid::Tag
6
6
  VALID_SYNTAX = %r!
7
7
  ([\w-]+)\s*=\s*
8
- (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
8
+ (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w.-]+))
9
9
  !x.freeze
10
10
  VARIABLE_SYNTAX = %r!
11
- (?<variable>[^{]*(\{\{\s*[\w\-\.]+\s*(\|.*)?\}\}[^\s{}]*)+)
11
+ (?<variable>[^{]*(\{\{\s*[\w\-.]+\s*(\|.*)?\}\}[^\s{}]*)+)
12
12
  (?<params>.*)
13
13
  !mx.freeze
14
14
 
15
15
  FULL_VALID_SYNTAX = %r!\A\s*(?:#{VALID_SYNTAX}(?=\s|\z)\s*)*\z!.freeze
16
- VALID_FILENAME_CHARS = %r!^[\w/\.-]+$!.freeze
16
+ VALID_FILENAME_CHARS = %r!^[\w/.-]+$!.freeze
17
17
  INVALID_SEQUENCES = %r![./]{2,}!.freeze
18
18
 
19
19
  def initialize(tag_name, markup, tokens)
20
20
  super
21
- matched = markup.strip.match(VARIABLE_SYNTAX)
21
+ markup = markup.strip
22
+ matched = markup.match(VARIABLE_SYNTAX)
22
23
  if matched
23
24
  @file = matched["variable"].strip
24
25
  @params = matched["params"].strip
25
26
  else
26
- @file, @params = markup.strip.split(%r!\s+!, 2)
27
+ @file, @params = markup.split(%r!\s+!, 2)
27
28
  end
28
29
  validate_params if @params
29
30
  @tag_name = tag_name
@@ -54,7 +55,7 @@ module Jekyll
54
55
  end
55
56
 
56
57
  def validate_file_name(file)
57
- if file =~ INVALID_SEQUENCES || file !~ VALID_FILENAME_CHARS
58
+ if INVALID_SEQUENCES.match?(file) || !VALID_FILENAME_CHARS.match?(file)
58
59
  raise ArgumentError, <<~MSG
59
60
  Invalid syntax for include tag. File contains invalid characters or sequences:
60
61
 
@@ -69,7 +70,7 @@ module Jekyll
69
70
  end
70
71
 
71
72
  def validate_params
72
- unless @params =~ FULL_VALID_SYNTAX
73
+ unless FULL_VALID_SYNTAX.match?(@params)
73
74
  raise ArgumentError, <<~MSG
74
75
  Invalid syntax for include tag:
75
76
 
@@ -90,7 +91,7 @@ module Jekyll
90
91
 
91
92
  # Render the variable if required
92
93
  def render_variable(context)
93
- Liquid::Template.parse(@file).render(context) if @file =~ VARIABLE_SYNTAX
94
+ Liquid::Template.parse(@file).render(context) if VARIABLE_SYNTAX.match?(@file)
94
95
  end
95
96
 
96
97
  def tag_includes_dirs(context)
@@ -100,7 +101,7 @@ module Jekyll
100
101
  def locate_include_file(context, file, safe)
101
102
  includes_dirs = tag_includes_dirs(context)
102
103
  includes_dirs.each do |dir|
103
- path = File.join(dir.to_s, file.to_s)
104
+ path = PathManager.join(dir, file)
104
105
  return path if valid_include_file?(path, dir.to_s, safe)
105
106
  end
106
107
  raise IOError, could_not_locate_message(file, includes_dirs, safe)
@@ -176,7 +177,7 @@ module Jekyll
176
177
 
177
178
  # This method allows to modify the file content by inheriting from the class.
178
179
  def read_file(file, context)
179
- File.read(file, file_read_opts(context))
180
+ File.read(file, **file_read_opts(context))
180
181
  end
181
182
 
182
183
  private
@@ -192,6 +193,60 @@ module Jekyll
192
193
  end
193
194
  end
194
195
 
196
+ # Do not inherit from this class.
197
+ # TODO: Merge into the `Jekyll::Tags::IncludeTag` in v5.0
198
+ class OptimizedIncludeTag < IncludeTag
199
+ def render(context)
200
+ @site ||= context.registers[:site]
201
+
202
+ file = render_variable(context) || @file
203
+ validate_file_name(file)
204
+
205
+ @site.inclusions[file] ||= locate_include_file(file)
206
+ inclusion = @site.inclusions[file]
207
+
208
+ add_include_to_dependency(inclusion, context) if @site.incremental?
209
+
210
+ context.stack do
211
+ context["include"] = parse_params(context) if @params
212
+ inclusion.render(context)
213
+ end
214
+ end
215
+
216
+ private
217
+
218
+ def locate_include_file(file)
219
+ @site.includes_load_paths.each do |dir|
220
+ path = PathManager.join(dir, file)
221
+ return Inclusion.new(@site, dir, file) if valid_include_file?(path, dir)
222
+ end
223
+ raise IOError, could_not_locate_message(file, @site.includes_load_paths, @site.safe)
224
+ end
225
+
226
+ def valid_include_file?(path, dir)
227
+ File.file?(path) && !outside_scope?(path, dir)
228
+ end
229
+
230
+ def outside_scope?(path, dir)
231
+ @site.safe && !realpath_prefixed_with?(path, dir)
232
+ end
233
+
234
+ def realpath_prefixed_with?(path, dir)
235
+ File.realpath(path).start_with?(dir)
236
+ rescue StandardError
237
+ false
238
+ end
239
+
240
+ def add_include_to_dependency(inclusion, context)
241
+ return unless context.registers[:page]&.key?("path")
242
+
243
+ @site.regenerator.add_dependency(
244
+ @site.in_source_dir(context.registers[:page]["path"]),
245
+ inclusion.path
246
+ )
247
+ end
248
+ end
249
+
195
250
  class IncludeRelativeTag < IncludeTag
196
251
  def tag_includes_dirs(context)
197
252
  Array(page_path(context)).freeze
@@ -209,6 +264,7 @@ module Jekyll
209
264
  else
210
265
  File.join(site.config["collections_dir"], page_payload["path"])
211
266
  end
267
+ resource_path.sub!(%r!/#excerpt\z!, "")
212
268
  site.in_source_dir File.dirname(resource_path)
213
269
  end
214
270
  end
@@ -216,5 +272,5 @@ module Jekyll
216
272
  end
217
273
  end
218
274
 
219
- Liquid::Template.register_tag("include", Jekyll::Tags::IncludeTag)
275
+ Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
220
276
  Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
@@ -57,6 +57,8 @@ module Jekyll
57
57
  end
58
58
 
59
59
  class PostUrl < Liquid::Tag
60
+ include Jekyll::Filters::URLFilters
61
+
60
62
  def initialize(tag_name, post, tokens)
61
63
  super
62
64
  @orig_post = post.strip
@@ -72,24 +74,25 @@ module Jekyll
72
74
  end
73
75
 
74
76
  def render(context)
77
+ @context = context
75
78
  site = context.registers[:site]
76
79
 
77
- site.posts.docs.each do |p|
78
- return p.url if @post == p
80
+ site.posts.docs.each do |document|
81
+ return relative_url(document) if @post == document
79
82
  end
80
83
 
81
84
  # New matching method did not match, fall back to old method
82
85
  # with deprecation warning if this matches
83
86
 
84
- site.posts.docs.each do |p|
85
- next unless @post.deprecated_equality p
87
+ site.posts.docs.each do |document|
88
+ next unless @post.deprecated_equality document
86
89
 
87
90
  Jekyll::Deprecator.deprecation_message "A call to "\
88
91
  "'{% post_url #{@post.name} %}' did not match " \
89
92
  "a post using the new matching method of checking name " \
90
93
  "(path-date-slug) equality. Please make sure that you " \
91
94
  "change this tag to match the post's name exactly."
92
- return p.url
95
+ return relative_url(document)
93
96
  end
94
97
 
95
98
  raise Jekyll::Errors::PostURLError, <<~MSG
@@ -4,13 +4,13 @@ module Jekyll
4
4
  class Theme
5
5
  extend Forwardable
6
6
  attr_reader :name
7
+
7
8
  def_delegator :gemspec, :version, :version
8
9
 
9
10
  def initialize(name)
10
11
  @name = name.downcase.strip
11
12
  Jekyll.logger.debug "Theme:", name
12
13
  Jekyll.logger.debug "Theme source:", root
13
- configure_sass
14
14
  end
15
15
 
16
16
  def root
@@ -22,6 +22,11 @@ module Jekyll
22
22
  "or includes a symbolic link loop"
23
23
  end
24
24
 
25
+ # The name of theme directory
26
+ def basename
27
+ @basename ||= File.basename(root)
28
+ end
29
+
25
30
  def includes_path
26
31
  @includes_path ||= path_for "_includes"
27
32
  end
@@ -38,13 +43,6 @@ module Jekyll
38
43
  @assets_path ||= path_for "assets"
39
44
  end
40
45
 
41
- def configure_sass
42
- return unless sass_path
43
-
44
- External.require_with_graceful_fail("sass") unless defined?(Sass)
45
- Sass.load_paths << sass_path
46
- end
47
-
48
46
  def runtime_dependencies
49
47
  gemspec.runtime_dependencies
50
48
  end
@@ -62,11 +60,22 @@ module Jekyll
62
60
  # escape the theme root.
63
61
  # However, symlinks are allowed to point to other directories within the theme.
64
62
  Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
65
- rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
66
- Jekyll.logger.warn "Invalid theme folder:", folder
63
+ rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
64
+ log_realpath_exception(e, folder)
67
65
  nil
68
66
  end
69
67
 
68
+ def log_realpath_exception(err, folder)
69
+ return if err.is_a?(Errno::ENOENT)
70
+
71
+ case err
72
+ when Errno::EACCES
73
+ Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
74
+ when Errno::ELOOP
75
+ Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
76
+ end
77
+ end
78
+
70
79
  def gemspec
71
80
  @gemspec ||= Gem::Specification.find_by_name(name)
72
81
  rescue Gem::LoadError
@@ -129,6 +129,8 @@ module Jekyll
129
129
  #
130
130
  # Returns the escaped path.
131
131
  def self.escape_path(path)
132
+ return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path)
133
+
132
134
  # Because URI.escape doesn't escape "?", "[" and "]" by default,
133
135
  # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
134
136
  #
@@ -139,8 +141,7 @@ module Jekyll
139
141
  # pct-encoded = "%" HEXDIG HEXDIG
140
142
  # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
141
143
  # / "*" / "+" / "," / ";" / "="
142
- path = Addressable::URI.encode(path)
143
- path.encode("utf-8").sub("#", "%23")
144
+ Addressable::URI.encode(path).encode("utf-8").sub("#", "%23")
144
145
  end
145
146
 
146
147
  # Unescapes a URL path segment
@@ -154,7 +155,10 @@ module Jekyll
154
155
  #
155
156
  # Returns the unescaped path.
156
157
  def self.unescape_path(path)
157
- Addressable::URI.unencode(path.encode("utf-8"))
158
+ path = path.encode("utf-8")
159
+ return path unless path.include?("%")
160
+
161
+ Addressable::URI.unencode(path)
158
162
  end
159
163
  end
160
164
  end
@@ -13,18 +13,11 @@ module Jekyll
13
13
  # Constants for use in #slugify
14
14
  SLUGIFY_MODES = %w(raw default pretty ascii latin).freeze
15
15
  SLUGIFY_RAW_REGEXP = Regexp.new('\\s+').freeze
16
- SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^[:alnum:]]+").freeze
17
- SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
16
+ SLUGIFY_DEFAULT_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}]+").freeze
17
+ SLUGIFY_PRETTY_REGEXP = Regexp.new("[^\\p{M}\\p{L}\\p{Nd}._~!$&'()+,;=@]+").freeze
18
18
  SLUGIFY_ASCII_REGEXP = Regexp.new("[^[A-Za-z0-9]]+").freeze
19
19
 
20
- # Takes an indented string and removes the preceding spaces on each line
21
-
22
- def strip_heredoc(str)
23
- str.gsub(%r!^[ \t]{#{(str.scan(%r!^[ \t]*(?=\S)!).min || "").size}}!, "")
24
- end
25
-
26
20
  # Takes a slug and turns it into a simple title.
27
-
28
21
  def titleize_slug(slug)
29
22
  slug.split("-").map!(&:capitalize).join(" ")
30
23
  end
@@ -75,11 +68,14 @@ module Jekyll
75
68
  #
76
69
  # Returns an array
77
70
  def pluralized_array_from_hash(hash, singular_key, plural_key)
78
- [].tap do |array|
79
- value = value_from_singular_key(hash, singular_key)
80
- value ||= value_from_plural_key(hash, plural_key)
81
- array << value
82
- end.flatten.compact
71
+ array = []
72
+ value = value_from_singular_key(hash, singular_key)
73
+ value ||= value_from_plural_key(hash, plural_key)
74
+
75
+ array << value
76
+ array.flatten!
77
+ array.compact!
78
+ array
83
79
  end
84
80
 
85
81
  def value_from_singular_key(hash, key)
@@ -140,9 +136,9 @@ module Jekyll
140
136
  # Determines whether a given file has
141
137
  #
142
138
  # Returns true if the YAML front matter is present.
143
- # rubocop: disable PredicateName
139
+ # rubocop: disable Naming/PredicateName
144
140
  def has_yaml_header?(file)
145
- !!(File.open(file, "rb", &:readline) =~ %r!\A---\s*\r?\n!)
141
+ File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n!
146
142
  rescue EOFError
147
143
  false
148
144
  end
@@ -155,7 +151,7 @@ module Jekyll
155
151
 
156
152
  content.include?("{%") || content.include?("{{")
157
153
  end
158
- # rubocop: enable PredicateName
154
+ # rubocop: enable Naming/PredicateName
159
155
 
160
156
  # Slugify a filename or title.
161
157
  #
@@ -219,7 +215,7 @@ module Jekyll
219
215
  slug = replace_character_sequence_with_hyphen(string, :mode => mode)
220
216
 
221
217
  # Remove leading/trailing hyphen
222
- slug.gsub!(%r!^\-|\-$!i, "")
218
+ slug.gsub!(%r!^-|-$!i, "")
223
219
 
224
220
  slug.downcase! unless cased
225
221
  Jekyll.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty?
@@ -72,7 +72,7 @@ module Jekyll
72
72
  def proc_version
73
73
  @proc_version ||=
74
74
  begin
75
- Pathutil.new("/proc/version").read
75
+ File.read("/proc/version")
76
76
  rescue Errno::ENOENT, Errno::EACCES
77
77
  nil
78
78
  end