jekyll 3.1.6 → 3.2.0.pre.beta1

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.

Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +200 -74
  3. data/README.markdown +13 -13
  4. data/lib/jekyll.rb +12 -8
  5. data/lib/jekyll/cleaner.rb +11 -8
  6. data/lib/jekyll/collection.rb +3 -3
  7. data/lib/jekyll/commands/build.rb +15 -12
  8. data/lib/jekyll/commands/clean.rb +15 -16
  9. data/lib/jekyll/commands/doctor.rb +7 -7
  10. data/lib/jekyll/commands/help.rb +4 -3
  11. data/lib/jekyll/commands/new.rb +49 -14
  12. data/lib/jekyll/commands/new_theme.rb +33 -0
  13. data/lib/jekyll/commands/serve.rb +33 -27
  14. data/lib/jekyll/commands/serve/servlet.rb +2 -3
  15. data/lib/jekyll/configuration.rb +10 -34
  16. data/lib/jekyll/converter.rb +6 -2
  17. data/lib/jekyll/converters/markdown.rb +1 -1
  18. data/lib/jekyll/converters/markdown/kramdown_parser.rb +1 -0
  19. data/lib/jekyll/convertible.rb +6 -9
  20. data/lib/jekyll/document.rb +5 -1
  21. data/lib/jekyll/drops/document_drop.rb +7 -33
  22. data/lib/jekyll/drops/drop.rb +2 -26
  23. data/lib/jekyll/drops/jekyll_drop.rb +0 -12
  24. data/lib/jekyll/drops/site_drop.rb +1 -1
  25. data/lib/jekyll/entry_filter.rb +61 -15
  26. data/lib/jekyll/errors.rb +6 -0
  27. data/lib/jekyll/excerpt.rb +5 -2
  28. data/lib/jekyll/filters.rb +49 -8
  29. data/lib/jekyll/frontmatter_defaults.rb +2 -2
  30. data/lib/jekyll/hooks.rb +1 -0
  31. data/lib/jekyll/layout.rb +16 -1
  32. data/lib/jekyll/page.rb +1 -0
  33. data/lib/jekyll/plugin.rb +1 -1
  34. data/lib/jekyll/plugin_manager.rb +5 -5
  35. data/lib/jekyll/publisher.rb +4 -4
  36. data/lib/jekyll/readers/data_reader.rb +3 -2
  37. data/lib/jekyll/readers/layout_reader.rb +19 -3
  38. data/lib/jekyll/readers/post_reader.rb +5 -1
  39. data/lib/jekyll/regenerator.rb +5 -3
  40. data/lib/jekyll/renderer.rb +4 -6
  41. data/lib/jekyll/site.rb +47 -18
  42. data/lib/jekyll/static_file.rb +5 -1
  43. data/lib/jekyll/tags/include.rb +33 -31
  44. data/lib/jekyll/tags/link.rb +26 -0
  45. data/lib/jekyll/tags/post_url.rb +18 -8
  46. data/lib/jekyll/theme.rb +56 -0
  47. data/lib/jekyll/theme_builder.rb +117 -0
  48. data/lib/jekyll/utils.rb +2 -14
  49. data/lib/jekyll/version.rb +1 -1
  50. data/lib/site_template/_config.yml +8 -2
  51. data/lib/site_template/_includes/footer.html +3 -3
  52. data/lib/site_template/_includes/head.html +2 -2
  53. data/lib/site_template/_includes/header.html +3 -3
  54. data/lib/site_template/_layouts/default.html +3 -3
  55. data/lib/site_template/_layouts/page.html +1 -1
  56. data/lib/site_template/_layouts/post.html +1 -1
  57. data/lib/site_template/_sass/_base.scss +11 -17
  58. data/lib/site_template/index.html +1 -1
  59. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
  60. data/lib/theme_template/Gemfile +2 -0
  61. data/lib/theme_template/LICENSE.txt.erb +21 -0
  62. data/lib/theme_template/README.md.erb +46 -0
  63. data/lib/theme_template/Rakefile.erb +74 -0
  64. data/lib/theme_template/example/_config.yml.erb +1 -0
  65. data/lib/theme_template/example/_post.md +13 -0
  66. data/lib/theme_template/example/index.html +14 -0
  67. data/lib/theme_template/example/style.scss +7 -0
  68. data/lib/theme_template/theme.gemspec.erb +22 -0
  69. metadata +34 -7
  70. data/lib/jekyll/drops/excerpt_drop.rb +0 -15
@@ -7,7 +7,7 @@ module Jekyll
7
7
  DEFAULTS = {
8
8
  "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
9
9
  "no-store, no-cache, must-revalidate"
10
- }
10
+ }.freeze
11
11
 
12
12
  def initialize(server, root, callbacks)
13
13
  # So we can access them easily.
@@ -25,8 +25,7 @@ module Jekyll
25
25
  super || super(req, res, "#{basename}.html")
26
26
  end
27
27
 
28
- #
29
-
28
+ # rubocop:disable Style/MethodName
30
29
  def do_GET(req, res)
31
30
  rtn = super
32
31
  validate_and_ensure_charset(req, res)
@@ -44,6 +44,7 @@ module Jekyll
44
44
  'port' => '4000',
45
45
  'host' => '127.0.0.1',
46
46
  'baseurl' => '',
47
+ 'show_dir_listing' => false,
47
48
 
48
49
  # Output Configuration
49
50
  'permalink' => 'date',
@@ -71,24 +72,7 @@ module Jekyll
71
72
  'hard_wrap' => false,
72
73
  'footnote_nr' => 1
73
74
  }
74
- }.map { |k, v| [k, v.freeze] }].freeze
75
-
76
- class << self
77
- # Static: Produce a Configuration ready for use in a Site.
78
- # It takes the input, fills in the defaults where values do not
79
- # exist, and patches common issues including migrating options for
80
- # backwards compatiblity. Except where a key or value is being fixed,
81
- # the user configuration will override the defaults.
82
- #
83
- # user_config - a Hash or Configuration of overrides.
84
- #
85
- # Returns a Configuration filled with defaults and fixed for common
86
- # problems and backwards-compatibility.
87
- def from(user_config)
88
- Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys).
89
- fix_common_issues.add_default_collections
90
- end
91
- end
75
+ }]
92
76
 
93
77
  # Public: Turn all keys into string
94
78
  #
@@ -165,7 +149,7 @@ module Jekyll
165
149
  Jekyll.logger.info "Configuration file:", file
166
150
  next_config
167
151
  rescue SystemCallError
168
- if @default_config_file
152
+ if @default_config_file ||= nil
169
153
  Jekyll.logger.warn "Configuration file:", "none"
170
154
  {}
171
155
  else
@@ -185,7 +169,6 @@ module Jekyll
185
169
 
186
170
  begin
187
171
  files.each do |config_file|
188
- next if config_file.nil? or config_file.empty?
189
172
  new_config = read_config_file(config_file)
190
173
  configuration = Utils.deep_merge_hashes(configuration, new_config)
191
174
  end
@@ -245,6 +228,7 @@ module Jekyll
245
228
  end
246
229
 
247
230
  %w(include exclude).each do |option|
231
+ config[option] ||= []
248
232
  if config[option].is_a?(String)
249
233
  Jekyll::Deprecator.deprecation_message "The '#{option}' configuration option" \
250
234
  " must now be specified as an array, but you specified" \
@@ -252,7 +236,7 @@ module Jekyll
252
236
  " as a list of comma-separated values."
253
237
  config[option] = csv_to_array(config[option])
254
238
  end
255
- config[option].map!(&:to_s) if config[option]
239
+ config[option].map!(&:to_s)
256
240
  end
257
241
 
258
242
  if (config['kramdown'] || {}).key?('use_coderay')
@@ -287,22 +271,14 @@ module Jekyll
287
271
  def add_default_collections
288
272
  config = clone
289
273
 
290
- # It defaults to `{}`, so this is only if someone sets it to null manually.
291
274
  return config if config['collections'].nil?
292
275
 
293
- # Ensure we have a hash.
294
276
  if config['collections'].is_a?(Array)
295
277
  config['collections'] = Hash[config['collections'].map { |c| [c, {}] }]
296
278
  end
297
-
298
- config['collections'] = Utils.deep_merge_hashes(
299
- { 'posts' => {} }, config['collections']
300
- ).tap do |collections|
301
- collections['posts']['output'] = true
302
- if config['permalink']
303
- collections['posts']['permalink'] ||= style_to_permalink(config['permalink'])
304
- end
305
- end
279
+ config['collections']['posts'] ||= {}
280
+ config['collections']['posts']['output'] = true
281
+ config['collections']['posts']['permalink'] = style_to_permalink(config['permalink'])
306
282
 
307
283
  config
308
284
  end
@@ -310,8 +286,8 @@ module Jekyll
310
286
  def renamed_key(old, new, config, _ = nil)
311
287
  if config.key?(old)
312
288
  Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" \
313
- "option has been renamed to '#{new}'. Please update your config " \
314
- "file accordingly."
289
+ " option has been renamed to '#{new}'. Please update your config" \
290
+ " file accordingly."
315
291
  config[new] = config.delete(old)
316
292
  end
317
293
  end
@@ -8,7 +8,9 @@ module Jekyll
8
8
  #
9
9
  # Returns the String prefix.
10
10
  def self.highlighter_prefix(highlighter_prefix = nil)
11
- @highlighter_prefix = highlighter_prefix if highlighter_prefix
11
+ if !defined?(@highlighter_prefix) || !highlighter_prefix.nil?
12
+ @highlighter_prefix = highlighter_prefix
13
+ end
12
14
  @highlighter_prefix
13
15
  end
14
16
 
@@ -20,7 +22,9 @@ module Jekyll
20
22
  #
21
23
  # Returns the String suffix.
22
24
  def self.highlighter_suffix(highlighter_suffix = nil)
23
- @highlighter_suffix = highlighter_suffix if highlighter_suffix
25
+ if !defined?(@highlighter_suffix) || !highlighter_suffix.nil?
26
+ @highlighter_suffix = highlighter_suffix
27
+ end
24
28
  @highlighter_suffix
25
29
  end
26
30
 
@@ -6,7 +6,7 @@ module Jekyll
6
6
  safe true
7
7
 
8
8
  def setup
9
- return if @setup
9
+ return if @setup ||= false
10
10
  unless (@parser = get_processor)
11
11
  Jekyll.logger.error "Invalid Markdown processor given:", @config["markdown"]
12
12
  Jekyll.logger.info "", "Custom processors are not loaded in safe mode" if @config["safe"]
@@ -18,6 +18,7 @@ module Jekyll
18
18
  Jekyll::External.require_with_graceful_fail "kramdown"
19
19
  @main_fallback_highlighter = config["highlighter"] || "rouge"
20
20
  @config = config["kramdown"] || {}
21
+ @highlighter = nil
21
22
  setup
22
23
  end
23
24
 
@@ -39,9 +39,9 @@ module Jekyll
39
39
  filename = File.join(base, name)
40
40
 
41
41
  begin
42
- self.content = File.read(site.in_source_dir(base, name),
42
+ self.content = File.read(@path || site.in_source_dir(base, name),
43
43
  Utils.merged_file_read_opts(site, opts))
44
- if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
44
+ if content =~ Document::YAML_FRONT_MATTER_REGEXP
45
45
  self.content = $POSTMATCH
46
46
  self.data = SafeYAML.load(Regexp.last_match(1))
47
47
  end
@@ -209,18 +209,15 @@ module Jekyll
209
209
 
210
210
  used = Set.new([layout])
211
211
 
212
- # Reset the payload layout data to ensure it starts fresh for each page.
213
- payload["layout"] = nil
214
-
215
212
  while layout
216
213
  Jekyll.logger.debug "Rendering Layout:", path
217
214
  payload["content"] = output
218
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
215
+ payload["layout"] = Utils.deep_merge_hashes(payload["layout"] || {}, layout.data)
219
216
 
220
217
  self.output = render_liquid(layout.content,
221
- payload,
222
- info,
223
- File.join(site.config['layouts_dir'], layout.name))
218
+ payload,
219
+ info,
220
+ layout.relative_path)
224
221
 
225
222
  # Add layout to dependency tree
226
223
  site.regenerator.add_dependency(
@@ -68,7 +68,11 @@ module Jekyll
68
68
  end
69
69
 
70
70
  def date
71
- data['date'] ||= site.time
71
+ data['date'] ||= (draft? ? source_file_mtime : site.time)
72
+ end
73
+
74
+ def source_file_mtime
75
+ @source_file_mtime ||= File.mtime(path)
72
76
  end
73
77
 
74
78
  # Returns whether the document is a draft. This is only the case if
@@ -5,12 +5,10 @@ module Jekyll
5
5
  class DocumentDrop < Drop
6
6
  extend Forwardable
7
7
 
8
- NESTED_OBJECT_FIELD_BLACKLIST = %w{
9
- content output excerpt next previous
10
- }.freeze
11
-
12
8
  mutable false
13
9
 
10
+ def_delegator :@obj, :next_doc, :next
11
+ def_delegator :@obj, :previous_doc, :previous
14
12
  def_delegator :@obj, :relative_path, :path
15
13
  def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url
16
14
 
@@ -22,35 +20,11 @@ module Jekyll
22
20
  fallback_data['excerpt'].to_s
23
21
  end
24
22
 
25
- def previous
26
- @obj.previous_doc.to_liquid
27
- end
28
-
29
- def next
30
- @obj.next_doc.to_liquid
31
- end
32
-
33
- # Generate a Hash for use in generating JSON.
34
- # This is useful if fields need to be cleared before the JSON can generate.
35
- #
36
- # Returns a Hash ready for JSON generation.
37
- def hash_for_json(state = nil)
38
- to_h.tap do |hash|
39
- if state && state.depth >= 2
40
- hash["previous"] = collapse_document(hash["previous"]) if hash["previous"]
41
- hash["next"] = collapse_document(hash["next"]) if hash["next"]
42
- end
43
- end
44
- end
45
-
46
- # Generate a Hash which breaks the recursive chain.
47
- # Certain fields which are normally available are omitted.
48
- #
49
- # Returns a Hash with only non-recursive fields present.
50
- def collapse_document(doc)
51
- doc.keys.each_with_object({}) do |(key, _), result|
52
- result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
53
- end
23
+ def <=>(other)
24
+ return nil unless other.is_a? DocumentDrop
25
+ cmp = self['date'] <=> other['date']
26
+ cmp = self['path'] <=> other['path'] if cmp.nil? || cmp == 0
27
+ cmp
54
28
  end
55
29
 
56
30
  private
@@ -3,9 +3,7 @@
3
3
  module Jekyll
4
4
  module Drops
5
5
  class Drop < Liquid::Drop
6
- include Enumerable
7
-
8
- NON_CONTENT_METHODS = [:fallback_data, :collapse_document].freeze
6
+ NON_CONTENT_METHODS = [:[], :[]=, :inspect, :to_h, :fallback_data].freeze
9
7
 
10
8
  # Get or set whether the drop class is mutable.
11
9
  # Mutability determines whether or not pre-defined fields may be
@@ -88,7 +86,7 @@ module Jekyll
88
86
  # Returns an Array of strings which represent method-specific keys.
89
87
  def content_methods
90
88
  @content_methods ||= (
91
- self.class.instance_methods - Jekyll::Drops::Drop.instance_methods - NON_CONTENT_METHODS
89
+ self.class.instance_methods(false) - NON_CONTENT_METHODS
92
90
  ).map(&:to_s).reject do |method|
93
91
  method.end_with?("=")
94
92
  end
@@ -140,22 +138,6 @@ module Jekyll
140
138
  JSON.pretty_generate to_h
141
139
  end
142
140
 
143
- # Generate a Hash for use in generating JSON.
144
- # This is useful if fields need to be cleared before the JSON can generate.
145
- #
146
- # Returns a Hash ready for JSON generation.
147
- def hash_for_json(state = nil)
148
- to_h
149
- end
150
-
151
- # Generate a JSON representation of the Drop.
152
- #
153
- # Returns a JSON representation of the Drop in a String.
154
- def to_json(state = nil)
155
- require 'json'
156
- JSON.generate(hash_for_json(state), state)
157
- end
158
-
159
141
  # Collects all the keys and passes each to the block in turn.
160
142
  #
161
143
  # block - a block which accepts one argument, the key
@@ -165,12 +147,6 @@ module Jekyll
165
147
  keys.each(&block)
166
148
  end
167
149
 
168
- def each(&block)
169
- each_key.each do |key|
170
- yield key, self[key]
171
- end
172
- end
173
-
174
150
  def merge(other, &block)
175
151
  self.dup.tap do |me|
176
152
  if block.nil?
@@ -16,18 +16,6 @@ module Jekyll
16
16
  def environment
17
17
  Jekyll.env
18
18
  end
19
-
20
- def to_h
21
- @to_h ||= {
22
- "version" => version,
23
- "environment" => environment
24
- }
25
- end
26
-
27
- def to_json(state = nil)
28
- require 'json'
29
- JSON.generate(to_h, state)
30
- end
31
19
  end
32
20
  end
33
21
  end
@@ -28,7 +28,7 @@ module Jekyll
28
28
  end
29
29
 
30
30
  def collections
31
- @site_collections ||= @obj.collections.values.sort_by(&:label).map(&:to_liquid)
31
+ @site_collections ||= @obj.collections.values.map(&:to_liquid)
32
32
  end
33
33
 
34
34
  private
@@ -1,12 +1,15 @@
1
1
  module Jekyll
2
2
  class EntryFilter
3
- SPECIAL_LEADING_CHARACTERS = ['.', '_', '#'].freeze
4
-
5
3
  attr_reader :site
4
+ SPECIAL_LEADING_CHARACTERS = [
5
+ '.', '_', '#', '~'
6
+ ].freeze
6
7
 
7
8
  def initialize(site, base_directory = nil)
8
9
  @site = site
9
- @base_directory = derive_base_directory(@site, base_directory.to_s.dup)
10
+ @base_directory = derive_base_directory(
11
+ @site, base_directory.to_s.dup
12
+ )
10
13
  end
11
14
 
12
15
  def base_directory
@@ -14,14 +17,14 @@ module Jekyll
14
17
  end
15
18
 
16
19
  def derive_base_directory(site, base_dir)
17
- if base_dir.start_with?(site.source)
18
- base_dir[site.source] = ""
19
- end
20
+ base_dir[site.source] = "" if base_dir.start_with?(site.source)
20
21
  base_dir
21
22
  end
22
23
 
23
24
  def relative_to_source(entry)
24
- File.join(base_directory, entry)
25
+ File.join(
26
+ base_directory, entry
27
+ )
25
28
  end
26
29
 
27
30
  def filter(entries)
@@ -33,7 +36,9 @@ module Jekyll
33
36
  end
34
37
 
35
38
  def included?(entry)
36
- glob_include?(site.include, entry)
39
+ glob_include?(site.include,
40
+ entry
41
+ )
37
42
  end
38
43
 
39
44
  def special?(entry)
@@ -51,21 +56,62 @@ module Jekyll
51
56
  excluded
52
57
  end
53
58
 
59
+ # --
60
+ # Check if a file is a symlink.
61
+ # NOTE: This can be converted to allowing even in safe,
62
+ # since we use Pathutil#in_path? now.
63
+ # --
54
64
  def symlink?(entry)
55
- File.symlink?(entry) && site.safe
65
+ site.safe && File.symlink?(entry) && symlink_outside_site_source?(entry)
56
66
  end
57
67
 
58
- def ensure_leading_slash(path)
59
- path[0..0] == "/" ? path : "/#{path}"
68
+ # --
69
+ # NOTE: Pathutil#in_path? gets the realpath.
70
+ # @param [<Anything>] entry the entry you want to validate.
71
+ # Check if a path is outside of our given root.
72
+ # --
73
+ def symlink_outside_site_source?(entry)
74
+ !Pathutil.new(entry).in_path?(
75
+ site.in_source_dir
76
+ )
60
77
  end
61
78
 
79
+ # --
80
+ # Check if an entry matches a specific pattern and return true,false.
62
81
  # Returns true if path matches against any glob pattern.
63
- # Look for more detail about glob pattern in method File::fnmatch.
82
+ # --
64
83
  def glob_include?(enum, e)
65
- entry = ensure_leading_slash(e)
84
+ entry = Pathutil.new(site.in_source_dir).join(e)
66
85
  enum.any? do |exp|
67
- item = ensure_leading_slash(exp)
68
- File.fnmatch?(item, entry) || entry.start_with?(item)
86
+
87
+ # Users who send a Regexp knows what they want to
88
+ # exclude, so let them send a Regexp to exclude files,
89
+ # we will not bother caring if it works or not, it's
90
+ # on them at this point.
91
+
92
+ if exp.is_a?(Regexp)
93
+ entry =~ exp
94
+
95
+ else
96
+ item = Pathutil.new(site.in_source_dir).join(exp)
97
+
98
+ # If it's a directory they want to exclude, AKA
99
+ # ends with a "/" then we will go on to check and
100
+ # see if the entry falls within that path and
101
+ # exclude it if that's the case.
102
+
103
+ if e.end_with?("/")
104
+ entry.in_path?(
105
+ item
106
+ )
107
+
108
+ else
109
+ File.fnmatch?(item, entry) ||
110
+ entry.to_path.start_with?(
111
+ item
112
+ )
113
+ end
114
+ end
69
115
  end
70
116
  end
71
117
  end