jekyll 4.0.0.pre.beta1 → 4.2.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +204 -18
  3. data/README.markdown +2 -6
  4. data/lib/blank_template/_layouts/default.html +1 -1
  5. data/lib/jekyll.rb +6 -17
  6. data/lib/jekyll/cleaner.rb +3 -3
  7. data/lib/jekyll/collection.rb +2 -2
  8. data/lib/jekyll/command.rb +4 -2
  9. data/lib/jekyll/commands/doctor.rb +19 -15
  10. data/lib/jekyll/commands/new.rb +4 -4
  11. data/lib/jekyll/commands/new_theme.rb +0 -2
  12. data/lib/jekyll/commands/serve.rb +12 -1
  13. data/lib/jekyll/configuration.rb +18 -19
  14. data/lib/jekyll/converters/identity.rb +2 -2
  15. data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -1
  16. data/lib/jekyll/convertible.rb +30 -23
  17. data/lib/jekyll/document.rb +41 -19
  18. data/lib/jekyll/drops/collection_drop.rb +3 -3
  19. data/lib/jekyll/drops/document_drop.rb +4 -3
  20. data/lib/jekyll/drops/drop.rb +98 -20
  21. data/lib/jekyll/drops/site_drop.rb +3 -3
  22. data/lib/jekyll/drops/static_file_drop.rb +4 -4
  23. data/lib/jekyll/drops/url_drop.rb +11 -3
  24. data/lib/jekyll/entry_filter.rb +18 -7
  25. data/lib/jekyll/excerpt.rb +1 -1
  26. data/lib/jekyll/filters.rb +112 -28
  27. data/lib/jekyll/filters/url_filters.rb +45 -15
  28. data/lib/jekyll/frontmatter_defaults.rb +14 -19
  29. data/lib/jekyll/hooks.rb +22 -21
  30. data/lib/jekyll/inclusion.rb +32 -0
  31. data/lib/jekyll/layout.rb +5 -0
  32. data/lib/jekyll/liquid_renderer.rb +18 -15
  33. data/lib/jekyll/liquid_renderer/file.rb +10 -0
  34. data/lib/jekyll/liquid_renderer/table.rb +1 -64
  35. data/lib/jekyll/page.rb +42 -11
  36. data/lib/jekyll/page_excerpt.rb +25 -0
  37. data/lib/jekyll/path_manager.rb +53 -10
  38. data/lib/jekyll/profiler.rb +58 -0
  39. data/lib/jekyll/reader.rb +11 -6
  40. data/lib/jekyll/readers/collection_reader.rb +1 -0
  41. data/lib/jekyll/readers/data_reader.rb +4 -0
  42. data/lib/jekyll/readers/layout_reader.rb +1 -0
  43. data/lib/jekyll/readers/page_reader.rb +1 -0
  44. data/lib/jekyll/readers/post_reader.rb +2 -1
  45. data/lib/jekyll/readers/static_file_reader.rb +1 -0
  46. data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
  47. data/lib/jekyll/related_posts.rb +1 -1
  48. data/lib/jekyll/renderer.rb +15 -17
  49. data/lib/jekyll/site.rb +34 -10
  50. data/lib/jekyll/static_file.rb +17 -12
  51. data/lib/jekyll/tags/include.rb +82 -33
  52. data/lib/jekyll/tags/link.rb +2 -1
  53. data/lib/jekyll/tags/post_url.rb +3 -4
  54. data/lib/jekyll/theme.rb +6 -8
  55. data/lib/jekyll/url.rb +8 -5
  56. data/lib/jekyll/utils.rb +5 -5
  57. data/lib/jekyll/utils/platforms.rb +34 -49
  58. data/lib/jekyll/utils/win_tz.rb +1 -1
  59. data/lib/jekyll/version.rb +1 -1
  60. data/lib/theme_template/theme.gemspec.erb +1 -4
  61. metadata +34 -39
@@ -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
 
@@ -54,8 +55,8 @@ module Jekyll
54
55
  #
55
56
  # Returns destination file path.
56
57
  def destination(dest)
57
- dest = @site.in_dest_dir(dest)
58
- @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url))
58
+ @destination ||= {}
59
+ @destination[dest] ||= @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url))
59
60
  end
60
61
 
61
62
  def destination_rel_dir
@@ -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
@@ -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
@@ -35,20 +36,16 @@ module Jekyll
35
36
 
36
37
  def parse_params(context)
37
38
  params = {}
38
- markup = @params
39
-
40
- while (match = VALID_SYNTAX.match(markup))
41
- markup = markup[match.end(0)..-1]
42
-
43
- value = if match[2]
44
- match[2].gsub('\\"', '"')
45
- elsif match[3]
46
- match[3].gsub("\\'", "'")
47
- elsif match[4]
48
- 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]
49
46
  end
50
47
 
51
- params[match[1]] = value
48
+ params[key] = value
52
49
  end
53
50
  params
54
51
  end
@@ -176,7 +173,7 @@ module Jekyll
176
173
 
177
174
  # This method allows to modify the file content by inheriting from the class.
178
175
  def read_file(file, context)
179
- File.read(file, file_read_opts(context))
176
+ File.read(file, **file_read_opts(context))
180
177
  end
181
178
 
182
179
  private
@@ -192,30 +189,82 @@ module Jekyll
192
189
  end
193
190
  end
194
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
+ return unless context.registers[:page]&.key?("path")
238
+
239
+ @site.regenerator.add_dependency(
240
+ @site.in_source_dir(context.registers[:page]["path"]),
241
+ inclusion.path
242
+ )
243
+ end
244
+ end
245
+
195
246
  class IncludeRelativeTag < IncludeTag
196
247
  def tag_includes_dirs(context)
197
248
  Array(page_path(context)).freeze
198
249
  end
199
250
 
200
251
  def page_path(context)
201
- if context.registers[:page].nil?
202
- context.registers[:site].source
203
- else
204
- site = context.registers[:site]
205
- page_payload = context.registers[:page]
206
- resource_path = \
207
- if page_payload["collection"].nil?
208
- page_payload["path"]
209
- else
210
- File.join(site.config["collections_dir"], page_payload["path"])
211
- end
212
- resource_path.sub!(%r!/#excerpt\z!, "")
213
- site.in_source_dir File.dirname(resource_path)
214
- end
252
+ page, site = context.registers.values_at(:page, :site)
253
+ return site.source unless page
254
+
255
+ site.in_source_dir File.dirname(resource_path(page, site))
256
+ end
257
+
258
+ private
259
+
260
+ def resource_path(page, site)
261
+ path = page["path"]
262
+ path = File.join(site.config["collections_dir"], path) if page["collection"]
263
+ path.sub(%r!/#excerpt\z!, "")
215
264
  end
216
265
  end
217
266
  end
218
267
  end
219
268
 
220
- Liquid::Template.register_tag("include", Jekyll::Tags::IncludeTag)
269
+ Liquid::Template.register_tag("include", Jekyll::Tags::OptimizedIncludeTag)
221
270
  Liquid::Template.register_tag("include_relative", Jekyll::Tags::IncludeRelativeTag)
@@ -21,11 +21,12 @@ module Jekyll
21
21
  @context = context
22
22
  site = context.registers[:site]
23
23
  relative_path = Liquid::Template.parse(@relative_path).render(context)
24
+ relative_path_with_leading_slash = PathManager.join("", relative_path)
24
25
 
25
26
  site.each_site_file do |item|
26
27
  return relative_url(item) if item.relative_path == relative_path
27
28
  # This takes care of the case for static files that have a leading /
28
- return relative_url(item) if item.relative_path == "/#{relative_path}"
29
+ return relative_url(item) if item.relative_path == relative_path_with_leading_slash
29
30
  end
30
31
 
31
32
  raise ArgumentError, <<~MSG
@@ -16,9 +16,8 @@ module Jekyll
16
16
  "'#{name}' does not contain valid date and/or title."
17
17
  end
18
18
 
19
- escaped_slug = Regexp.escape(slug)
20
- @name_regex = %r!^_posts/#{path}#{date}-#{escaped_slug}\.[^.]+|
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
@@ -51,7 +50,7 @@ module Jekyll
51
50
  if path.nil? || path == ""
52
51
  other.data["slug"]
53
52
  else
54
- path + "/" + other.data["slug"]
53
+ "#{path}/#{other.data["slug"]}"
55
54
  end
56
55
  end
57
56
  end
@@ -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
@@ -94,7 +94,8 @@ module Jekyll
94
94
 
95
95
  def generate_url_from_drop(template)
96
96
  template.gsub(%r!:([a-z_]+)!) do |match|
97
- pool = possible_keys(match.sub(":", ""))
97
+ name = Regexp.last_match(1)
98
+ pool = name.end_with?("_") ? [name, name.chomp!("_")] : [name]
98
99
 
99
100
  winner = pool.find { |key| @placeholders.key?(key) }
100
101
  if winner.nil?
@@ -107,15 +108,17 @@ module Jekyll
107
108
  value = "" if value.nil?
108
109
  replacement = self.class.escape_path(value)
109
110
 
110
- match.sub(":#{winner}", replacement)
111
- end.squeeze("/")
111
+ match.sub!(":#{winner}", replacement)
112
+ end
112
113
  end
113
114
 
114
115
  # Returns a sanitized String URL, stripping "../../" and multiples of "/",
115
116
  # as well as the beginning "/" so we can enforce and ensure it.
116
-
117
117
  def sanitize_url(str)
118
- "/#{str}".gsub("..", "/").gsub("./", "").squeeze("/")
118
+ "/#{str}".gsub("..", "/").tap do |result|
119
+ result.gsub!("./", "")
120
+ result.squeeze!("/")
121
+ end
119
122
  end
120
123
 
121
124
  # Escapes a path to be a valid URL path segment
@@ -13,8 +13,8 @@ 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
20
  # Takes a slug and turns it into a simple title.
@@ -136,7 +136,7 @@ module Jekyll
136
136
  # Determines whether a given file has
137
137
  #
138
138
  # Returns true if the YAML front matter is present.
139
- # rubocop: disable PredicateName
139
+ # rubocop: disable Naming/PredicateName
140
140
  def has_yaml_header?(file)
141
141
  File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n!
142
142
  rescue EOFError
@@ -151,7 +151,7 @@ module Jekyll
151
151
 
152
152
  content.include?("{%") || content.include?("{{")
153
153
  end
154
- # rubocop: enable PredicateName
154
+ # rubocop: enable Naming/PredicateName
155
155
 
156
156
  # Slugify a filename or title.
157
157
  #
@@ -215,7 +215,7 @@ module Jekyll
215
215
  slug = replace_character_sequence_with_hyphen(string, :mode => mode)
216
216
 
217
217
  # Remove leading/trailing hyphen
218
- slug.gsub!(%r!^\-|\-$!i, "")
218
+ slug.gsub!(%r!^-|-$!i, "")
219
219
 
220
220
  slug.downcase! unless cased
221
221
  Jekyll.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty?
@@ -5,78 +5,63 @@ module Jekyll
5
5
  module Platforms
6
6
  extend self
7
7
 
8
- # Provides jruby? and mri? which respectively detect these two types of
9
- # tested Engines we support, in the future we might probably support the
10
- # other one that everyone used to talk about.
8
+ def jruby?
9
+ RUBY_ENGINE == "jruby"
10
+ end
11
11
 
12
- { :jruby? => "jruby", :mri? => "ruby" }.each do |k, v|
13
- define_method k do
14
- ::RUBY_ENGINE == v
15
- end
12
+ def mri?
13
+ RUBY_ENGINE == "ruby"
16
14
  end
17
15
 
18
- # --
19
- # Allows you to detect "real" Windows, or what we would consider
20
- # "real" Windows. That is, that we can pass the basic test and the
21
- # /proc/version returns nothing to us.
22
- # --
16
+ def windows?
17
+ vanilla_windows? || bash_on_windows?
18
+ end
23
19
 
20
+ # Not a Windows Subsystem for Linux (WSL)
24
21
  def vanilla_windows?
25
- RbConfig::CONFIG["host_os"] =~ %r!mswin|mingw|cygwin!i && \
26
- !proc_version
22
+ rbconfig_host.match?(%r!mswin|mingw|cygwin!) && proc_version.empty?
27
23
  end
24
+ alias_method :really_windows?, :vanilla_windows?
28
25
 
29
- # --
30
- # XXX: Remove in 4.0
31
- # --
32
-
33
- alias_method :really_windows?, \
34
- :vanilla_windows?
35
-
36
- #
37
-
26
+ # Determine if Windows Subsystem for Linux (WSL)
38
27
  def bash_on_windows?
39
- RbConfig::CONFIG["host_os"] =~ %r!linux! && \
40
- proc_version =~ %r!microsoft!i
28
+ linux_os? && microsoft_proc_version?
41
29
  end
42
30
 
43
- #
44
-
45
- def windows?
46
- vanilla_windows? || bash_on_windows?
47
- end
48
-
49
- #
50
-
51
31
  def linux?
52
- RbConfig::CONFIG["host_os"] =~ %r!linux! && \
53
- proc_version !~ %r!microsoft!i
32
+ linux_os? && !microsoft_proc_version?
54
33
  end
55
34
 
56
- # Provides windows?, linux?, osx?, unix? so that we can detect
57
- # platforms. This is mostly useful for `jekyll doctor` and for testing
58
- # where we kick off certain tests based on the platform.
59
-
60
- { :osx? => %r!darwin|mac os!, :unix? => %r!solaris|bsd! }.each do |k, v|
61
- define_method k do
62
- !!(
63
- RbConfig::CONFIG["host_os"] =~ v
64
- )
65
- end
35
+ def osx?
36
+ rbconfig_host.match?(%r!darwin|mac os!)
66
37
  end
67
38
 
68
- #
39
+ def unix?
40
+ rbconfig_host.match?(%r!solaris|bsd!)
41
+ end
69
42
 
70
43
  private
71
44
 
72
45
  def proc_version
73
- @proc_version ||=
46
+ @proc_version ||= \
74
47
  begin
75
- File.read("/proc/version")
48
+ File.read("/proc/version").downcase
76
49
  rescue Errno::ENOENT, Errno::EACCES
77
- nil
50
+ ""
78
51
  end
79
52
  end
53
+
54
+ def rbconfig_host
55
+ @rbconfig_host ||= RbConfig::CONFIG["host_os"].downcase
56
+ end
57
+
58
+ def linux_os?
59
+ rbconfig_host.include?("linux")
60
+ end
61
+
62
+ def microsoft_proc_version?
63
+ proc_version.include?("microsoft")
64
+ end
80
65
  end
81
66
  end
82
67
  end